/** * @file * @brief BACnet EventNotification encode and decode functions * @author John Minack * @author Steve Karg * @date 2008 * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */ #include #include "bacnet/event.h" #include "bacnet/bacdcode.h" #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 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 */ int uevent_notify_encode_apdu( uint8_t *apdu, const BACNET_EVENT_NOTIFICATION_DATA *data) { int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ if (apdu) { apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; apdu[1] = SERVICE_UNCONFIRMED_EVENT_NOTIFICATION; /* service choice */ } len = 2; apdu_len += len; if (apdu) { apdu += len; } len = event_notify_encode_service_request(apdu, data); if (len > 0) { apdu_len += len; } else { apdu_len = 0; } return apdu_len; } /** * @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 * @return number of bytes encoded, or zero if unable to encode */ int cevent_notify_encode_apdu( uint8_t *apdu, uint8_t invoke_id, const BACNET_EVENT_NOTIFICATION_DATA *data) { int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ if (apdu) { apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); apdu[2] = invoke_id; apdu[3] = SERVICE_CONFIRMED_EVENT_NOTIFICATION; /* service choice */ } len = 4; apdu_len += len; if (apdu) { apdu += len; } len = event_notify_encode_service_request(apdu, data); if (len > 0) { apdu_len += len; } else { apdu_len = 0; } return apdu_len; } #if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED /** * @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 */ int event_notify_encode_service_request( uint8_t *apdu, const BACNET_EVENT_NOTIFICATION_DATA *data) { 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; } /* tag 0 - processIdentifier */ len = encode_context_unsigned(apdu, 0, data->processIdentifier); apdu_len += len; if (apdu) { apdu += len; } /* tag 1 - initiatingObjectIdentifier */ len = encode_context_object_id( apdu, 1, data->initiatingObjectIdentifier.type, data->initiatingObjectIdentifier.instance); apdu_len += len; if (apdu) { apdu += len; } /* tag 2 - eventObjectIdentifier */ len = encode_context_object_id( apdu, 2, data->eventObjectIdentifier.type, data->eventObjectIdentifier.instance); apdu_len += len; if (apdu) { apdu += len; } /* tag 3 - timeStamp */ len = bacapp_encode_context_timestamp(apdu, 3, &data->timeStamp); apdu_len += len; if (apdu) { apdu += len; } /* tag 4 - noticicationClass */ len = encode_context_unsigned(apdu, 4, data->notificationClass); apdu_len += len; if (apdu) { apdu += len; } /* tag 5 - priority */ len = encode_context_unsigned(apdu, 5, data->priority); apdu_len += len; if (apdu) { apdu += len; } /* tag 6 - eventType */ len = encode_context_enumerated(apdu, 6, data->eventType); apdu_len += len; if (apdu) { apdu += len; } /* tag 7 - messageText */ if (data->messageText) { len = encode_context_character_string(apdu, 7, data->messageText); apdu_len += len; if (apdu) { apdu += len; } } /* tag 8 - notifyType */ len = encode_context_enumerated(apdu, 8, data->notifyType); apdu_len += len; if (apdu) { apdu += len; } switch (data->notifyType) { case NOTIFY_ALARM: case NOTIFY_EVENT: /* tag 9 - ackRequired */ len = encode_context_boolean(apdu, 9, data->ackRequired); apdu_len += len; if (apdu) { apdu += len; } /* tag 10 - fromState */ len = encode_context_enumerated(apdu, 10, data->fromState); apdu_len += len; if (apdu) { apdu += len; } break; default: break; } /* tag 11 - toState */ len = encode_context_enumerated(apdu, 11, data->toState); apdu_len += len; if (apdu) { apdu += len; } switch (data->notifyType) { case NOTIFY_ALARM: case NOTIFY_EVENT: /* tag 12 - event values */ len = encode_opening_tag(apdu, 12); apdu_len += len; if (apdu) { apdu += len; } switch (data->eventType) { case EVENT_CHANGE_OF_BITSTRING: len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 0, &data->notificationParams.changeOfBitstring .referencedBitString); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.changeOfBitstring .statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_CHANGE_OF_STATE: len = encode_opening_tag(apdu, 1); apdu_len += len; if (apdu) { apdu += len; } len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } len = bacapp_encode_property_state( apdu, &data->notificationParams.changeOfState.newState); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.changeOfState.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 1); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_CHANGE_OF_VALUE: len = encode_opening_tag(apdu, 2); apdu_len += len; if (apdu) { apdu += len; } len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } switch (data->notificationParams.changeOfValue.tag) { case CHANGE_OF_VALUE_REAL: len = encode_context_real( apdu, 1, data->notificationParams.changeOfValue.newValue .changeValue); apdu_len += len; if (apdu) { apdu += len; } break; case CHANGE_OF_VALUE_BITS: len = encode_context_bitstring( apdu, 0, &data->notificationParams.changeOfValue.newValue .changedBits); apdu_len += len; if (apdu) { apdu += len; } break; default: return 0; } len = encode_closing_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.changeOfValue.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 2); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_COMMAND_FAILURE: len = encode_opening_tag(apdu, 3); apdu_len += len; if (apdu) { apdu += len; } len = encode_opening_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } switch (data->notificationParams.commandFailure.tag) { case COMMAND_FAILURE_BINARY_PV: len = encode_application_enumerated( apdu, data->notificationParams.commandFailure .commandValue.binaryValue); apdu_len += len; if (apdu) { apdu += len; } break; case COMMAND_FAILURE_UNSIGNED: len = encode_application_unsigned( apdu, data->notificationParams.commandFailure .commandValue.unsignedValue); apdu_len += len; if (apdu) { apdu += len; } break; default: return 0; } len = encode_closing_tag(apdu, 0); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.commandFailure.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_opening_tag(apdu, 2); apdu_len += len; if (apdu) { apdu += len; } switch (data->notificationParams.commandFailure.tag) { case COMMAND_FAILURE_BINARY_PV: len = encode_application_enumerated( apdu, data->notificationParams.commandFailure .feedbackValue.binaryValue); apdu_len += len; if (apdu) { apdu += len; } break; case COMMAND_FAILURE_UNSIGNED: len = encode_application_unsigned( apdu, data->notificationParams.commandFailure .feedbackValue.unsignedValue); apdu_len += len; if (apdu) { apdu += len; } break; default: return 0; } len = encode_closing_tag(apdu, 2); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 3); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_FLOATING_LIMIT: len = encode_opening_tag(apdu, 4); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 0, data->notificationParams.floatingLimit.referenceValue); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.floatingLimit.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 2, data->notificationParams.floatingLimit.setPointValue); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 3, data->notificationParams.floatingLimit.errorLimit); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 4); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_OUT_OF_RANGE: len = encode_opening_tag(apdu, 5); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 0, data->notificationParams.outOfRange.exceedingValue); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.outOfRange.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 2, data->notificationParams.outOfRange.deadband); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_real( apdu, 3, data->notificationParams.outOfRange.exceededLimit); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 5); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_CHANGE_OF_LIFE_SAFETY: len = encode_opening_tag(apdu, 8); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_enumerated( apdu, 0, data->notificationParams.changeOfLifeSafety.newState); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_enumerated( apdu, 1, data->notificationParams.changeOfLifeSafety.newMode); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 2, &data->notificationParams.changeOfLifeSafety .statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_enumerated( apdu, 3, data->notificationParams.changeOfLifeSafety .operationExpected); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 8); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_BUFFER_READY: len = encode_opening_tag(apdu, 10); apdu_len += len; if (apdu) { apdu += len; } len = bacapp_encode_context_device_obj_property_ref( apdu, 0, &data->notificationParams.bufferReady.bufferProperty); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_unsigned( apdu, 1, data->notificationParams.bufferReady .previousNotification); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_unsigned( apdu, 2, data->notificationParams.bufferReady .currentNotification); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 10); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_UNSIGNED_RANGE: len = encode_opening_tag(apdu, 11); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_unsigned( apdu, 0, data->notificationParams.unsignedRange.exceedingValue); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.unsignedRange.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_unsigned( apdu, 2, data->notificationParams.unsignedRange.exceededLimit); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, 11); apdu_len += len; if (apdu) { apdu += len; } break; case EVENT_ACCESS_EVENT: len = encode_opening_tag(apdu, 13); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_enumerated( apdu, 0, data->notificationParams.accessEvent.accessEvent); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_bitstring( apdu, 1, &data->notificationParams.accessEvent.statusFlags); apdu_len += len; if (apdu) { apdu += len; } len = encode_context_unsigned( apdu, 2, data->notificationParams.accessEvent.accessEventTag); apdu_len += len; if (apdu) { apdu += len; } len = bacapp_encode_context_timestamp( apdu, 3, &data->notificationParams.accessEvent.accessEventTime); apdu_len += len; if (apdu) { apdu += len; } len = bacapp_encode_context_device_obj_ref( apdu, 4, &data->notificationParams.accessEvent.accessCredential); apdu_len += len; if (apdu) { apdu += len; } if (data->notificationParams.accessEvent .authenticationFactor.format_type < AUTHENTICATION_FACTOR_MAX) { len = bacapp_encode_context_authentication_factor( apdu, 5, &data->notificationParams.accessEvent .authenticationFactor); apdu_len += len; if (apdu) { apdu += len; } } len = encode_closing_tag(apdu, 13); apdu_len += len; if (apdu) { 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: 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); apdu_len += len; break; case NOTIFY_ACK_NOTIFICATION: /* FIXME: handle this case */ default: break; } return apdu_len; } /** * @brief Encode the EventNotification service request * @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 * @return number of bytes encoded, or zero if unable to encode or too large */ size_t event_notification_service_request_encode( uint8_t *apdu, size_t apdu_size, const BACNET_EVENT_NOTIFICATION_DATA *data) { size_t apdu_len = 0; /* total length of the apdu, return value */ apdu_len = event_notify_encode_service_request(NULL, data); if (apdu_len > apdu_size) { apdu_len = 0; } else { apdu_len = event_notify_encode_service_request(apdu, data); } return apdu_len; } /** * @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_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_size, BACNET_EVENT_NOTIFICATION_DATA *data) { 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; 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) { if (data) { data->processIdentifier = (uint32_t)unsigned_value; } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } /* 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 { return BACNET_STATUS_ERROR; } /* 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 { return BACNET_STATUS_ERROR; } /* 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 { return BACNET_STATUS_ERROR; } /* 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) { if (data) { data->notificationClass = (uint32_t)unsigned_value; } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } /* 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) { if (data) { data->priority = (uint8_t)unsigned_value; } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } /* 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 BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } /* message-text[7] CharacterString OPTIONAL */ if (data) { cstring = data->messageText; } else { cstring = NULL; } 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; } 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; } } else if (len == 0) { /* OPTIONAL - set default out of range */ if (data) { data->fromState = EVENT_STATE_MAX; } } else { return BACNET_STATUS_ERROR; } } /* 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 { return BACNET_STATUS_ERROR; } } 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 } 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 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; } } 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; } } 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; } } 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; 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; } } 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; } } 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; } } 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_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 = (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; } 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; } /* 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; } #endif break; } #if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED if (property_value) { /* load the next value store */ property_value = property_value->next; } #endif } } 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; } /* 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 apdu_len; }