289 lines
9.6 KiB
C
289 lines
9.6 KiB
C
/**
|
|
* @file
|
|
* @brief BACnetAccessRule service encode and decode
|
|
* @author Nikola Jelic <nikola.jelic@euroicc.com>
|
|
* @date 2015
|
|
* @copyright SPDX-License-Identifier: MIT
|
|
*/
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include "bacnet/access_rule.h"
|
|
#include "bacnet/bacdcode.h"
|
|
|
|
/**
|
|
* @brief Encode the BACnetAccessRule
|
|
*
|
|
* BACnetAccessRule ::= SEQUENCE {
|
|
* time-range-specifier [0] ENUMERATED {
|
|
* specified (0),
|
|
* always (1)
|
|
* },
|
|
* time-range[1] BACnetDeviceObjectPropertyReference OPTIONAL,
|
|
* -- to be present if time-range-specifier has the value "specified"
|
|
* location-specifier [2] ENUMERATED {
|
|
* specified (0),
|
|
* all (1)
|
|
* },
|
|
* location [3] BACnetDeviceObjectReference OPTIONAL,
|
|
* -- to be present if location-specifier has the value "specified"
|
|
* enable [4] BOOLEAN
|
|
* }
|
|
*
|
|
* @param apdu Pointer to the buffer for encoding, or NULL for length
|
|
* @param rule Pointer to the data to be encoded
|
|
* @return number of bytes encoded
|
|
*/
|
|
int bacapp_encode_access_rule(uint8_t *apdu, const BACNET_ACCESS_RULE *rule)
|
|
{
|
|
int len;
|
|
int apdu_len = 0;
|
|
|
|
len = encode_context_enumerated(apdu, 0, rule->time_range_specifier);
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
|
len = bacapp_encode_context_device_obj_property_ref(
|
|
apdu, 1, &rule->time_range);
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
}
|
|
len = encode_context_enumerated(apdu, 2, rule->location_specifier);
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
|
len = bacapp_encode_context_device_obj_ref(apdu, 3, &rule->location);
|
|
apdu_len += len;
|
|
if (apdu) {
|
|
apdu += len;
|
|
}
|
|
}
|
|
len = encode_context_boolean(apdu, 4, rule->enable);
|
|
apdu_len += len;
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @brief Decode the BACnetAccessRule
|
|
* @param apdu Pointer to the buffer for decoding.
|
|
* @param apdu_size The size of the buffer for decoding.
|
|
* @param data Pointer to the data to be stored
|
|
* @return number of bytes decoded or BACNET_STATUS_ERROR on error
|
|
*/
|
|
int bacnet_access_rule_decode(
|
|
const uint8_t *apdu, size_t apdu_size, BACNET_ACCESS_RULE *data)
|
|
{
|
|
int len;
|
|
int apdu_len = 0;
|
|
uint32_t enumerated_value = 0, time_range_specifier = 0,
|
|
location_specifier = 0;
|
|
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *reference = NULL;
|
|
BACNET_DEVICE_OBJECT_REFERENCE *location_reference = NULL;
|
|
bool boolean_value = false;
|
|
|
|
/* time-range-specifier [0] ENUMERATED {
|
|
specified (0),
|
|
always (1)
|
|
}
|
|
*/
|
|
len = bacnet_enumerated_context_decode(
|
|
&apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value);
|
|
if (len <= 0) {
|
|
return BACNET_STATUS_ERROR;
|
|
} else if (enumerated_value < TIME_RANGE_SPECIFIER_MAX) {
|
|
time_range_specifier = enumerated_value;
|
|
if (data) {
|
|
data->time_range_specifier =
|
|
(BACNET_ACCESS_RULE_TIME_RANGE_SPECIFIER)time_range_specifier;
|
|
}
|
|
} else {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
apdu_len += len;
|
|
/* time-range[1] BACnetDeviceObjectPropertyReference OPTIONAL,
|
|
-- to be present if time-range-specifier has the value "specified" */
|
|
if (time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
|
if (data) {
|
|
reference = &data->time_range;
|
|
}
|
|
len = bacnet_device_object_property_reference_context_decode(
|
|
&apdu[apdu_len], apdu_size - apdu_len, 1, reference);
|
|
if (len <= 0) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
apdu_len += len;
|
|
}
|
|
/* time-range-specifier [0] ENUMERATED {
|
|
specified (0),
|
|
always (1)
|
|
}
|
|
*/
|
|
len = bacnet_enumerated_context_decode(
|
|
&apdu[apdu_len], apdu_size - apdu_len, 2, &enumerated_value);
|
|
if (len <= 0) {
|
|
return BACNET_STATUS_ERROR;
|
|
} else if (enumerated_value < LOCATION_SPECIFIER_MAX) {
|
|
location_specifier = enumerated_value;
|
|
if (data) {
|
|
data->location_specifier =
|
|
(BACNET_ACCESS_RULE_LOCATION_SPECIFIER)location_specifier;
|
|
}
|
|
} else {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
apdu_len += len;
|
|
/* location [3] BACnetDeviceObjectReference OPTIONAL,
|
|
-- to be present if location-specifier has the value "specified" */
|
|
if (location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
|
if (data) {
|
|
location_reference = &data->location;
|
|
}
|
|
len = bacnet_device_object_reference_context_decode(
|
|
&apdu[apdu_len], apdu_size - apdu_len, 3, location_reference);
|
|
if (len <= 0) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
apdu_len += len;
|
|
}
|
|
/* enable [4] BOOLEAN */
|
|
len = bacnet_boolean_context_decode(
|
|
&apdu[apdu_len], apdu_size - apdu_len, 4, &boolean_value);
|
|
if (len <= 0) {
|
|
return BACNET_STATUS_ERROR;
|
|
}
|
|
if (data) {
|
|
data->enable = boolean_value;
|
|
}
|
|
apdu_len += len;
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/**
|
|
* @brief Decode the BACnetAccessRule
|
|
* @param apdu Pointer to the buffer for decoding.
|
|
* @param data Pointer to the data to be stored
|
|
* @return number of bytes decoded
|
|
* @deprecated Use bacapp_access_rule_decode() instead
|
|
*/
|
|
int bacapp_decode_access_rule(const uint8_t *apdu, BACNET_ACCESS_RULE *data)
|
|
{
|
|
return bacnet_access_rule_decode(apdu, MAX_APDU, data);
|
|
}
|
|
|
|
/**
|
|
* @brief Parse a string into a BACnetAccessRule value
|
|
* @param value [out] The BACnetAccessRule value
|
|
* @param argv [in] The string to parse
|
|
* @return True on success, else False
|
|
*/
|
|
bool bacnet_access_rule_from_ascii(BACNET_ACCESS_RULE *value, const char *argv)
|
|
{
|
|
int count = 0;
|
|
unsigned long device_instance1 = 0, instance1 = 0, property1 = 0;
|
|
unsigned long device_instance2 = 0, instance2 = 0;
|
|
unsigned int object_type1 = 0, object_type2 = 0;
|
|
|
|
if (!value || !argv) {
|
|
return false;
|
|
}
|
|
/* BACnetDeviceObjectPropertyReference or OPTIONAL=ALWAYS */
|
|
/* BACnetDeviceObjectReference or OPTIONAL=ALL */
|
|
if (strcmp(argv, "always,all") == 0) {
|
|
value->time_range_specifier = TIME_RANGE_SPECIFIER_ALWAYS;
|
|
value->location_specifier = LOCATION_SPECIFIER_ALL;
|
|
return true;
|
|
}
|
|
count = sscanf(
|
|
argv, "%7lu:%4u:%7lu:%7lu,%7lu:%4u:%7lu", &device_instance1,
|
|
&object_type1, &instance1, &property1, &device_instance2, &object_type2,
|
|
&instance2);
|
|
if (count == 7) {
|
|
value->time_range_specifier = TIME_RANGE_SPECIFIER_SPECIFIED;
|
|
value->time_range.deviceIdentifier.type = OBJECT_DEVICE;
|
|
value->time_range.deviceIdentifier.instance = device_instance1;
|
|
value->time_range.objectIdentifier.type = object_type1;
|
|
value->time_range.objectIdentifier.instance = instance1;
|
|
value->time_range.propertyIdentifier = property1;
|
|
value->location_specifier = LOCATION_SPECIFIER_SPECIFIED;
|
|
value->location.deviceIdentifier.type = OBJECT_DEVICE;
|
|
value->location.deviceIdentifier.instance = instance1;
|
|
value->location.objectIdentifier.type = object_type2;
|
|
value->location.objectIdentifier.instance = instance2;
|
|
return true;
|
|
}
|
|
count = sscanf(
|
|
argv, "%7lu:%4u:%7lu:%7lu,all", &device_instance1, &object_type1,
|
|
&instance1, &property1);
|
|
if (count == 5) {
|
|
value->time_range_specifier = TIME_RANGE_SPECIFIER_SPECIFIED;
|
|
value->time_range.deviceIdentifier.type = OBJECT_DEVICE;
|
|
value->time_range.deviceIdentifier.instance = device_instance1;
|
|
value->time_range.objectIdentifier.type = object_type1;
|
|
value->time_range.objectIdentifier.instance = instance1;
|
|
value->time_range.propertyIdentifier = property1;
|
|
value->location_specifier = LOCATION_SPECIFIER_ALL;
|
|
return true;
|
|
}
|
|
count = sscanf(
|
|
argv, "always,%7lu:%4u:%7lu", &device_instance2, &object_type2,
|
|
&instance2);
|
|
if (count == 3) {
|
|
value->time_range_specifier = TIME_RANGE_SPECIFIER_ALWAYS;
|
|
value->location_specifier = LOCATION_SPECIFIER_SPECIFIED;
|
|
value->location.deviceIdentifier.type = OBJECT_DEVICE;
|
|
value->location.deviceIdentifier.instance = device_instance2;
|
|
value->location.objectIdentifier.type = object_type2;
|
|
value->location.objectIdentifier.instance = instance2;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Compare two BACnetAccessRule values
|
|
* @param value1 [in] The first BACnetAccessRule value
|
|
* @param value2 [in] The second BACnetAccessRule value
|
|
* @return True if the values are the same, else False
|
|
*/
|
|
bool bacnet_access_rule_same(
|
|
const BACNET_ACCESS_RULE *value1, const BACNET_ACCESS_RULE *value2)
|
|
{
|
|
bool status;
|
|
|
|
if (value1->time_range_specifier != value2->time_range_specifier) {
|
|
return false;
|
|
}
|
|
if (value1->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
|
status = bacnet_device_object_property_reference_same(
|
|
&value1->time_range, &value2->time_range);
|
|
if (!status) {
|
|
return false;
|
|
}
|
|
}
|
|
if (value1->location_specifier != value2->location_specifier) {
|
|
return false;
|
|
}
|
|
if (value1->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
|
status = bacnet_device_object_reference_same(
|
|
&value1->location, &value2->location);
|
|
if (!status) {
|
|
return false;
|
|
}
|
|
}
|
|
if (value1->enable != value2->enable) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|