Files
bacnet_stack/src/bacnet/bacaudit.c
T
2025-06-23 12:18:13 -05:00

1096 lines
34 KiB
C

/**
* @file
* @brief BACnetActionCommand codec used by Command objects
* @author Steve Karg <skarg@users.sourceforge.net>
* @date November 2024
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/* 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, &timestamp);
if (len > 0) {
apdu_len += len;
#ifdef BACNET_AUDIT_NOTIFICATION_SOURCE_TIMESTAMP_ENABLE
if (value) {
bacapp_timestamp_copy(&value->source_timestamp, &timestamp);
}
#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, &timestamp);
if (len > 0) {
apdu_len += len;
#ifdef BACNET_AUDIT_NOTIFICATION_TARGET_TIMESTAMP_ENABLE
if (value) {
bacapp_timestamp_copy(&value->target_timestamp, &timestamp);
}
#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,
&notification);
if (len > 0) {
if (value) {
memmove(
&value->log_datum.notification, &notification,
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;
}