/** * @file * @brief BACnetActionCommand codec used by Command objects * @author Steve Karg * @date November 2024 * @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 */ #include #include #include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacdcode.h" #include "bacnet/bactimevalue.h" #include "bacnet/timestamp.h" /* me! */ #include "bacnet/bacaudit.h" /** * @brief Encode the BACnetAuditValue * @param apdu - buffer of data to be encoded, or NULL for length * @param value - value to be encoded * @return the number of apdu bytes encoded */ int bacnet_audit_value_encode(uint8_t *apdu, const BACNET_AUDIT_VALUE *value) { /* total length of the apdu, return value */ int apdu_len = 0; if (!value) { return 0; } switch (value->tag) { case BACNET_APPLICATION_TAG_BOOLEAN: apdu_len = encode_application_boolean(apdu, value->type.boolean_value); break; case BACNET_APPLICATION_TAG_UNSIGNED_INT: apdu_len = encode_application_unsigned(apdu, value->type.unsigned_value); break; case BACNET_APPLICATION_TAG_SIGNED_INT: apdu_len = encode_application_signed(apdu, value->type.integer_value); break; case BACNET_APPLICATION_TAG_REAL: apdu_len = encode_application_real(apdu, value->type.real_value); break; case BACNET_APPLICATION_TAG_ENUMERATED: apdu_len = encode_application_enumerated( apdu, value->type.enumerated_value); break; case BACNET_APPLICATION_TAG_NULL: default: if (apdu) { apdu[0] = value->tag; } apdu_len++; break; } return apdu_len; } /** * @brief Encode the BACnetAuditValue * @param apdu - buffer of data to be encoded, or NULL for length * @param tag_number - context tag number to be encoded * @param value - value to be encoded * @return the number of apdu bytes encoded */ int bacnet_audit_value_context_encode( uint8_t *apdu, uint8_t tag_number, const BACNET_AUDIT_VALUE *value) { int len; int apdu_len = 0; len = encode_opening_tag(apdu, tag_number); apdu_len += len; if (apdu) { apdu += len; } len = bacnet_audit_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 the BACnetAuditValue * @param apdu - buffer of data to be encoded, or NULL for length * @param tag_number - context tag number to be encoded * @param value - value to be encoded * @return the number of apdu bytes encoded, or BACNET_STATUS_ERROR if an error * occurs */ int bacnet_audit_value_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_AUDIT_VALUE *value) { int len = 0; int apdu_len = 0; BACNET_TAG tag = { 0 }; bool boolean_value = false; float real_value = 0.0f; uint32_t enumerated_value = 0; BACNET_UNSIGNED_INTEGER unsigned_value = 0; int32_t integer_value = 0; if (!value) { return BACNET_STATUS_ERROR; } if (!apdu) { return BACNET_STATUS_ERROR; } len = bacnet_tag_decode(apdu, apdu_size, &tag); if ((len > 0) && tag.application) { if (value) { value->tag = tag.number; } switch (tag.number) { case BACNET_APPLICATION_TAG_BOOLEAN: apdu_len = bacnet_boolean_application_decode( apdu, apdu_size, &boolean_value); if (apdu_len > 0) { if (value) { value->type.boolean_value = boolean_value; } } break; case BACNET_APPLICATION_TAG_UNSIGNED_INT: apdu_len = bacnet_unsigned_application_decode( apdu, apdu_size, &unsigned_value); if (apdu_len > 0) { if (value) { value->type.unsigned_value = unsigned_value; } } break; case BACNET_APPLICATION_TAG_SIGNED_INT: apdu_len = bacnet_signed_application_decode( apdu, apdu_size, &integer_value); if (apdu_len > 0) { if (value) { value->type.integer_value = integer_value; } } break; case BACNET_APPLICATION_TAG_REAL: apdu_len = bacnet_real_application_decode( apdu, apdu_size, &real_value); if (apdu_len > 0) { if (value) { value->type.real_value = real_value; } } break; case BACNET_APPLICATION_TAG_ENUMERATED: apdu_len = bacnet_enumerated_application_decode( apdu, apdu_size, &enumerated_value); if (apdu_len > 0) { if (value) { value->type.enumerated_value = enumerated_value; } } break; case BACNET_APPLICATION_TAG_NULL: default: apdu_len = len; break; } } else { apdu_len = BACNET_STATUS_ERROR; } return apdu_len; } /** * @brief Decode a context encoded BACnetAuditValue from a buffer * @param apdu - the APDU buffer * @param apdu_size - the size of the APDU buffer * @param tag_number - the tag number * @param value - BACnetAuditValue to decode into * @return number of bytes decoded, zero if tag mismatch, or * BACNET_STATUS_ERROR on failure. */ int bacnet_audit_value_context_decode( const uint8_t *apdu, uint16_t apdu_size, uint8_t tag_number, BACNET_AUDIT_VALUE *value) { int len = 0; int apdu_len = 0; if (!apdu) { return BACNET_STATUS_ERROR; } if (!bacnet_is_opening_tag_number( &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { return 0; } apdu_len += len; len = bacnet_audit_value_decode(&apdu[apdu_len], apdu_size - apdu_len, value); if (len > 0) { apdu_len += len; if (bacnet_is_closing_tag_number( &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { apdu_len += len; } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } return apdu_len; } /** * @brief Compare two BACnetActionPropertyValue complex datatypes * @param value1 [in] The first structure to compare * @param value2 [in] The second structure to compare * @return true if the two structures are the same */ bool bacnet_audit_value_same( const BACNET_AUDIT_VALUE *value1, const BACNET_AUDIT_VALUE *value2) { bool status = false; /*return value */ if ((value1 == NULL) || (value2 == NULL)) { return false; } if (value1->tag == value2->tag) { switch (value1->tag) { case BACNET_APPLICATION_TAG_NULL: status = true; break; case BACNET_APPLICATION_TAG_BOOLEAN: status = value1->type.boolean_value == value2->type.boolean_value; break; case BACNET_APPLICATION_TAG_UNSIGNED_INT: status = value1->type.unsigned_value == value2->type.unsigned_value; break; case BACNET_APPLICATION_TAG_SIGNED_INT: status = value1->type.integer_value == value2->type.integer_value; break; case BACNET_APPLICATION_TAG_REAL: status = !islessgreater( value1->type.real_value, value2->type.real_value); break; case BACNET_APPLICATION_TAG_ENUMERATED: status = value1->type.enumerated_value == value2->type.enumerated_value; break; default: break; } } return status; } /** * @brief Encode the BACnetAuditNotification * @param apdu - buffer of data to be encoded, or NULL for length * @param value - value to be encoded * @return the number of apdu bytes encoded */ int bacnet_audit_log_notification_encode( uint8_t *apdu, const BACNET_AUDIT_NOTIFICATION *value) { int len, apdu_len = 0; /* total length of the apdu, return value */ if (!value) { return 0; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_TIMESTAMP_ENABLE /* source-timestamp [0] BACnetTimeStamp OPTIONAL */ len = bacapp_encode_context_timestamp(apdu, 0, &value->source_timestamp); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_TIMESTAMP_ENABLE /* target-timestamp [1] BACnetTimeStamp OPTIONAL */ len = bacapp_encode_context_timestamp(apdu, 1, &value->target_timestamp); apdu_len += len; if (apdu) { apdu += len; } #endif /* source-device [2] BACnetRecipient */ len = bacnet_recipient_context_encode(apdu, 2, &value->source_device); apdu_len += len; if (apdu) { apdu += len; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_OBJECT_ENABLE /* source-object [3] BACnetObjectIdentifier OPTIONAL */ len = encode_context_object_id( apdu, 3, value->source_object.type, value->source_object.instance); apdu_len += len; if (apdu) { apdu += len; } #endif /* operation [4] BACnetAuditOperation */ len = encode_context_unsigned(apdu, 4, value->operation); apdu_len += len; if (apdu) { apdu += len; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_COMMENT_ENABLE /* source-comment [5] CharacterString OPTIONAL */ len = encode_context_character_string(apdu, 5, &value->source_comment); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_COMMENT_ENABLE /* target-comment [6] CharacterString OPTIONAL */ len = encode_context_character_string(apdu, 6, &value->target_comment); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_INVOKE_ID_ENABLE /* invoke-id [7] Unsigned8 OPTIONAL */ len = encode_context_unsigned(apdu, 7, value->invoke_id); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ID_ENABLE /* source-user-id [8] Unsigned16 OPTIONAL */ len = encode_context_unsigned(apdu, 8, value->source_user_id); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ROLE_ENABLE /* source-user-role [9] Unsigned8 OPTIONAL */ len = encode_context_unsigned(apdu, 9, value->source_user_role); apdu_len += len; if (apdu) { apdu += len; } #endif /* target-device [10] BACnetRecipient */ len = bacnet_recipient_context_encode(apdu, 10, &value->target_device); apdu_len += len; if (apdu) { apdu += len; } #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_OBJECT_ENABLE /* target-object [11] BACnetObjectIdentifier OPTIONAL */ len = encode_context_object_id( apdu, 11, value->target_object.type, value->target_object.instance); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PROPERTY_ENABLE /* target-property [12] BACnetPropertyReference OPTIONAL */ len = bacnet_property_reference_context_encode( apdu, 12, &value->target_property); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PRIORITY_ENABLE /* target-priority [13] Unsigned (1..16) OPTIONAL */ len = encode_context_unsigned(apdu, 13, value->target_priority); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_VALUE_ENABLE /* target-value [14] ABSTRACT-SYNTAX.&Type OPTIONAL */ len = bacnet_audit_value_context_encode(apdu, 14, &value->target_value); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_CURRENT_VALUE_ENABLE /* current-value [15] ABSTRACT-SYNTAX.&Type OPTIONAL */ len = bacnet_audit_value_context_encode(apdu, 15, &value->current_value); apdu_len += len; if (apdu) { apdu += len; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_RESULT_ENABLE /* result [16] Error OPTIONAL */ len = encode_context_enumerated(apdu, 16, value->result); apdu_len += len; #endif return apdu_len; } /** * @brief Decode the BACnetAuditNotification * @param apdu - buffer of data to be decoded * @param value - value to hold the decoded data * @return the number of apdu bytes encoded, or BACNET_STATUS_ERROR if an error * occurs */ int bacnet_audit_log_notification_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_AUDIT_NOTIFICATION *value) { int len = 0; int apdu_len = 0; BACNET_UNSIGNED_INTEGER unsigned_value; BACNET_TIMESTAMP timestamp; BACNET_OBJECT_TYPE object_type; uint32_t object_instance; BACNET_CHARACTER_STRING char_string; BACNET_RECIPIENT recipient; struct BACnetPropertyReference property_reference; BACNET_AUDIT_VALUE audit_value; uint32_t enumerated_value; if (!apdu) { return BACNET_STATUS_ERROR; } /* source-timestamp [0] BACnetTimeStamp OPTIONAL */ len = bacnet_timestamp_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 0, ×tamp); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_TIMESTAMP_ENABLE if (value) { bacapp_timestamp_copy(&value->source_timestamp, ×tamp); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-timestamp [1] BACnetTimeStamp OPTIONAL */ len = bacnet_timestamp_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 1, ×tamp); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_TIMESTAMP_ENABLE if (value) { bacapp_timestamp_copy(&value->target_timestamp, ×tamp); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* source-device [2] BACnetRecipient */ len = bacnet_recipient_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 2, &recipient); if (len > 0) { apdu_len += len; if (value) { bacnet_recipient_copy(&value->source_device, &recipient); } } else { return BACNET_STATUS_ERROR; } /* source-object [3] BACnetObjectIdentifier OPTIONAL */ len = bacnet_object_id_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 3, &object_type, &object_instance); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_OBJECT_ENABLE if (value) { value->source_object.type = object_type; value->source_object.instance = object_instance; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* operation [4] BACnetAuditOperation */ len = bacnet_unsigned_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 4, &unsigned_value); if (len > 0) { if (unsigned_value < AUDIT_OPERATION_MAX) { if (value) { value->operation = (BACNET_AUDIT_OPERATION)unsigned_value; } } else { return BACNET_STATUS_ERROR; } apdu_len += len; } else { return BACNET_STATUS_ERROR; } /* source-comment [5] CharacterString OPTIONAL */ len = bacnet_character_string_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 5, &char_string); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_COMMENT_ENABLE if (value) { characterstring_copy(&value->source_comment, &char_string); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-comment [6] CharacterString OPTIONAL */ len = bacnet_character_string_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 6, &char_string); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_COMMENT_ENABLE if (value) { characterstring_copy(&value->target_comment, &char_string); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* invoke-id [7] Unsigned8 OPTIONAL */ len = bacnet_unsigned_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 7, &unsigned_value); if (len > 0) { apdu_len += len; if (unsigned_value > UINT8_MAX) { return BACNET_STATUS_ERROR; } #ifdef BACNET_AUDIT_NOTIFICATION_INVOKE_ID_ENABLE if (value) { value->invoke_id = (uint8_t)unsigned_value; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* source-user-id [8] Unsigned16 OPTIONAL */ if (bacnet_is_context_tag_number( &apdu[apdu_len], apdu_size - apdu_len, 8, NULL, NULL)) { len = bacnet_unsigned_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 8, &unsigned_value); if (len > 0) { apdu_len += len; if (unsigned_value > UINT16_MAX) { return BACNET_STATUS_ERROR; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ID_ENABLE if (value) { value->source_user_id = (uint16_t)unsigned_value; } #endif } else { return BACNET_STATUS_ERROR; } } /* source-user-role [9] Unsigned8 OPTIONAL */ len = bacnet_unsigned_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 9, &unsigned_value); if (len > 0) { apdu_len += len; if (unsigned_value > UINT8_MAX) { return BACNET_STATUS_ERROR; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ROLE_ENABLE if (value) { value->source_user_role = (uint8_t)unsigned_value; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-device [10] BACnetRecipient */ len = bacnet_recipient_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 10, &recipient); if (len > 0) { apdu_len += len; if (value) { bacnet_recipient_copy(&value->target_device, &recipient); } } else { return BACNET_STATUS_ERROR; } /* target-object [11] BACnetObjectIdentifier OPTIONAL */ len = bacnet_object_id_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 11, &object_type, &object_instance); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_OBJECT_ENABLE if (value) { value->target_object.type = object_type; value->target_object.instance = object_instance; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-property [12] BACnetPropertyReference OPTIONAL */ len = bacnet_property_reference_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 12, &property_reference); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PROPERTY_ENABLE if (value) { memcpy( &value->target_property, &property_reference, sizeof(property_reference)); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-priority [13] Unsigned (1..16) OPTIONAL */ len = bacnet_unsigned_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 13, &unsigned_value); if (len > 0) { apdu_len += len; if (unsigned_value > UINT8_MAX) { return BACNET_STATUS_ERROR; } #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PRIORITY_ENABLE if (value) { value->target_priority = (uint8_t)unsigned_value; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* target-value [14] ABSTRACT-SYNTAX.&Type OPTIONAL */ len = bacnet_audit_value_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 14, &audit_value); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_VALUE_ENABLE if (value) { memcpy(&value->target_value, &audit_value, sizeof(audit_value)); } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } /* current-value [15] ABSTRACT-SYNTAX.&Type OPTIONAL */ len = bacnet_audit_value_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 15, &audit_value); if (len > 0) { apdu_len += len; #ifdef BACNET_AUDIT_NOTIFICATION_CURRENT_VALUE_ENABLE if (value) { memcpy(&value->current_value, &audit_value, sizeof(audit_value)); } #endif } /* result [16] Error OPTIONAL */ len = bacnet_enumerated_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 16, &enumerated_value); if (len > 0) { apdu_len += len; if (enumerated_value > UINT16_MAX) { return BACNET_STATUS_ERROR; } #ifdef BACNET_AUDIT_NOTIFICATION_RESULT_ENABLE if (value) { value->result = (BACNET_ERROR_CODE)enumerated_value; } #endif } else if (len < 0) { return BACNET_STATUS_ERROR; } return apdu_len; } /** * @brief Decode a context BACnetAuditLogNotification context data. * Check for an opening tag and a closing tag as well. * * @param apdu Pointer to the buffer containing the encoded value * @param apdu_size Size of the buffer containing the encoded value * @param tag_number Tag number * @param value Pointer to the structure that shall be decoded into. * * @return number of bytes decoded, zero if tag mismatch, * or #BACNET_STATUS_ERROR (-1) if malformed */ int bacnet_audit_log_notification_context_decode( const uint8_t *apdu, uint16_t apdu_size, uint8_t tag_number, BACNET_AUDIT_NOTIFICATION *value) { int len = 0; int apdu_len = 0; if (!apdu) { return BACNET_STATUS_ERROR; } if (!bacnet_is_opening_tag_number( &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { return 0; } apdu_len += len; len = bacnet_audit_log_notification_decode( &apdu[apdu_len], apdu_size - apdu_len, value); if (len > 0) { apdu_len += len; if (bacnet_is_closing_tag_number( &apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) { apdu_len += len; } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } return apdu_len; } /** * @brief Compare two BACnetAuditNotification for same-ness * @param value1 - value 1 structure * @param value2 - value 2 structure * @return true if the values are the same */ bool bacnet_audit_log_notification_same( const BACNET_AUDIT_NOTIFICATION *value1, const BACNET_AUDIT_NOTIFICATION *value2) { if (!value1 || !value2) { return false; } if (value1->operation != value2->operation) { return false; } if (!bacnet_recipient_same( &value1->source_device, &value2->source_device)) { return false; } if (!bacnet_recipient_same( &value1->target_device, &value2->target_device)) { return false; } #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_TIMESTAMP_ENABLE if (!bacapp_timestamp_same( &value1->source_timestamp, &value2->source_timestamp)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_TIMESTAMP_ENABLE if (!bacapp_timestamp_same( &value1->target_timestamp, &value2->target_timestamp)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_OBJECT_ENABLE if (!bacnet_object_id_same( value1->source_object.type, value1->source_object.instance, value2->source_object.type, value2->source_object.instance)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_COMMENT_ENABLE if (!characterstring_same( &value1->source_comment, &value2->source_comment)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_COMMENT_ENABLE if (!characterstring_same( &value1->target_comment, &value2->target_comment)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_INVOKE_ID_ENABLE if (value1->invoke_id != value2->invoke_id) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ID_ENABLE if (value1->source_user_id != value2->source_user_id) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_USER_ROLE_ENABLE if (value1->source_user_role != value2->source_user_role) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_OBJECT_ENABLE if (!bacnet_object_id_same( value1->target_object.type, value1->target_object.instance, value2->target_object.type, value2->target_object.instance)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PROPERTY_ENABLE if (!bacnet_property_reference_same( &value1->target_property, &value2->target_property)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_PRIORITY_ENABLE if (value1->target_priority != value2->target_priority) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_TARGET_VALUE_ENABLE if (!bacnet_audit_value_same( &value1->target_value, &value2->target_value)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_CURRENT_VALUE_ENABLE if (!bacnet_audit_value_same( &value1->current_value, &value2->current_value)) { return false; } #endif #ifdef BACNET_AUDIT_NOTIFICATION_RESULT_ENABLE if (value1->result != value2->result) { return false; } #endif return true; } /** * @brief Encode property value according to the application tag * * BACnetAuditLogRecord ::= SEQUENCE { * timestamp [0] BACnetDateTime, * log-datum [1] CHOICE { * log-status [0] BACnetLogStatus, * audit-notification [1] BACnetAuditNotification, * time-change [2] REAL * } * } * * @param apdu - Pointer to the buffer to encode to, or NULL for length * @param value - Pointer to the property value to encode from * @return number of bytes encoded */ int bacnet_audit_log_record_encode( uint8_t *apdu, const BACNET_AUDIT_LOG_RECORD *value) { int len, apdu_len = 0; /* total length of the apdu, return value */ BACNET_BIT_STRING log_status = { 0 }; if (!value) { return 0; } /* timestamp [0] BACnetDateTime */ len = bacapp_encode_context_datetime(apdu, 0, &value->timestamp); apdu_len += len; if (apdu) { apdu += len; } /* log-datum [1] CHOICE */ len = encode_opening_tag(apdu, 1); apdu_len += len; if (apdu) { apdu += len; } switch (value->tag) { case AUDIT_LOG_DATUM_TAG_STATUS: /* log-status [0] BACnetLogStatus */ bitstring_bits_used_set(&log_status, LOG_STATUS_MAX); bitstring_set_octet(&log_status, 0, value->log_datum.log_status); len = encode_context_bitstring(apdu, value->tag, &log_status); apdu_len += len; if (apdu) { apdu += len; } break; case AUDIT_LOG_DATUM_TAG_NOTIFICATION: /* audit-notification [1] BACnetAuditNotification */ len = encode_opening_tag(apdu, value->tag); apdu_len += len; if (apdu) { apdu += len; } len = bacnet_audit_log_notification_encode( apdu, &value->log_datum.notification); apdu_len += len; if (apdu) { apdu += len; } len = encode_closing_tag(apdu, value->tag); apdu_len += len; if (apdu) { apdu += len; } break; case AUDIT_LOG_DATUM_TAG_TIME_CHANGE: /* time-change [2] REAL */ len = encode_context_real( apdu, value->tag, value->log_datum.time_change); apdu_len += len; if (apdu) { apdu += len; } break; default: break; } /* log-datum [1] CHOICE */ len = encode_closing_tag(apdu, 1); apdu_len += len; return apdu_len; } /** * @brief Decode property value from the application buffer * @param apdu - Pointer to the buffer to decode from * @param apdu_size Size of the buffer to decode from * @param value - Pointer to the property value to decode to * @return number of bytes encoded, or BACNET_STATUS_ERROR if an error * occurs */ int bacnet_audit_log_record_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_AUDIT_LOG_RECORD *value) { int len = 0; int apdu_len = 0; BACNET_TAG tag = { 0 }; BACNET_DATE_TIME bdatetime = { 0 }; BACNET_BIT_STRING log_status = { 0 }; BACNET_AUDIT_NOTIFICATION notification = { 0 }; float real_value = 0.0f; if (!apdu) { return BACNET_STATUS_ERROR; } /* timestamp [0] BACnetDateTime */ len = bacnet_datetime_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 0, &bdatetime); if (len > 0) { if (value) { datetime_copy(&value->timestamp, &bdatetime); } apdu_len += len; } else { return BACNET_STATUS_ERROR; } /* log-datum [1] CHOICE */ if (bacnet_is_opening_tag_number( &apdu[apdu_len], apdu_size - apdu_len, 1, &len)) { apdu_len += len; } else { return BACNET_STATUS_ERROR; } len = bacnet_tag_decode(&apdu[apdu_len], apdu_size - apdu_len, &tag); if (len <= 0) { return BACNET_STATUS_ERROR; } if (value) { value->tag = tag.number; } /* ignore the len. len is included in context decoder below. */ switch (tag.number) { case AUDIT_LOG_DATUM_TAG_STATUS: /* log-status [0] BACnetLogStatus */ len = bacnet_bitstring_context_decode( &apdu[apdu_len], apdu_size - apdu_len, tag.number, &log_status); if (len > 0) { if (value) { value->log_datum.log_status = bitstring_octet(&log_status, 0); } apdu_len += len; } else { return BACNET_STATUS_ERROR; } break; case AUDIT_LOG_DATUM_TAG_NOTIFICATION: /* audit-notification [1] BACnetAuditNotification */ len = bacnet_audit_log_notification_context_decode( &apdu[apdu_len], apdu_size - apdu_len, tag.number, ¬ification); if (len > 0) { if (value) { memmove( &value->log_datum.notification, ¬ification, sizeof(BACNET_AUDIT_NOTIFICATION)); } apdu_len += len; } else { return BACNET_STATUS_ERROR; } break; case AUDIT_LOG_DATUM_TAG_TIME_CHANGE: /* time-change [2] REAL */ len = bacnet_real_context_decode( &apdu[apdu_len], apdu_size - apdu_len, tag.number, &real_value); if (len > 0) { if (value) { value->log_datum.time_change = real_value; } apdu_len += len; } else { return BACNET_STATUS_ERROR; } break; default: return BACNET_STATUS_ERROR; } /* log-datum [1] CHOICE */ if (bacnet_is_closing_tag_number( &apdu[apdu_len], apdu_size - apdu_len, 1, &len)) { apdu_len += len; } else { return BACNET_STATUS_ERROR; } return apdu_len; } /** * @brief Compare two BACnetActionPropertyValue complex datatypes * @param value1 [in] The first structure to compare * @param value2 [in] The second structure to compare * @return true if the two structures are the same */ bool bacnet_audit_log_record_same( const BACNET_AUDIT_LOG_RECORD *value1, const BACNET_AUDIT_LOG_RECORD *value2) { bool status = false; /*return value */ if ((value1 == NULL) || (value2 == NULL)) { return false; } /* does the tag match? */ if (value1->tag == value2->tag) { status = true; } if (status) { status = false; /* does the timestamp match? */ if (datetime_compare(&value1->timestamp, &value2->timestamp) == 0) { status = true; } } if (status) { status = false; switch (value1->tag) { case AUDIT_LOG_DATUM_TAG_STATUS: if (value1->log_datum.log_status == value2->log_datum.log_status) { status = true; } break; case AUDIT_LOG_DATUM_TAG_NOTIFICATION: if (bacnet_audit_log_notification_same( &value1->log_datum.notification, &value2->log_datum.notification)) { status = true; } break; case AUDIT_LOG_DATUM_TAG_TIME_CHANGE: if (!islessgreater( value1->log_datum.time_change, value2->log_datum.time_change)) { status = true; } break; default: status = false; break; } } return status; }