cb243c36a8
* Change MIT license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change GPL exception license texts to SPDX-License-Identifier SPDX-License-Identifier is much easier to understand and grep than license text so use that instead. * Change misc license texts to SPDX-License-Identifier There are some external code in repo which are not licenses as most of the stuff in this repo. We still want every file to have SPDX identifier to easily grep licenses. * Add currently used license files Even though Bacnet-Stack is using SPDX identifiers we still need to give those license files with source. For this reason add all license files to license/ folder. SPDX has also files which would make same thing but this is style which example Linux kernel is using and it is quite clear so I choose that one for now. I choosed not yet bring CC-PDDC as that is not right license for those files. --------- Co-authored-by: Kari Argillander <kari.argillander@fidelix.com>
299 lines
11 KiB
C
299 lines
11 KiB
C
/**************************************************************************
|
|
*
|
|
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
*********************************************************************/
|
|
|
|
/* Binary Value Objects - customize for your use */
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include "bacnet/bacdef.h"
|
|
#include "bacnet/bacdcode.h"
|
|
#include "bacnet/bacenum.h"
|
|
#include "bacnet/config.h" /* the custom stuff */
|
|
#include "bacnet/wp.h"
|
|
#include "bacnet/rp.h"
|
|
#include "bacnet/basic/object/bv.h"
|
|
|
|
#define MAX_BINARY_VALUES 8
|
|
|
|
static BACNET_BINARY_PV Present_Value[MAX_BINARY_VALUES];
|
|
|
|
static void Binary_Value_Initialize(void)
|
|
{
|
|
static bool initialized = false;
|
|
unsigned i;
|
|
|
|
if (!initialized) {
|
|
initialized = true;
|
|
for (i = 0; i < MAX_BINARY_VALUES; i++) {
|
|
Present_Value[i] = BINARY_INACTIVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* we simply have 0-n object instances. */
|
|
bool Binary_Value_Valid_Instance(uint32_t object_instance)
|
|
{
|
|
if (object_instance < MAX_BINARY_VALUES)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* we simply have 0-n object instances. */
|
|
unsigned Binary_Value_Count(void)
|
|
{
|
|
return MAX_BINARY_VALUES;
|
|
}
|
|
|
|
/* we simply have 0-n object instances. */
|
|
uint32_t Binary_Value_Index_To_Instance(unsigned index)
|
|
{
|
|
return index;
|
|
}
|
|
|
|
/* we simply have 0-n object instances. */
|
|
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
|
|
{
|
|
unsigned index = MAX_BINARY_VALUES;
|
|
|
|
if (object_instance < MAX_BINARY_VALUES)
|
|
index = object_instance;
|
|
|
|
return index;
|
|
}
|
|
|
|
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
|
|
{
|
|
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
|
|
|
Binary_Value_Initialize();
|
|
if (object_instance < MAX_BINARY_VALUES) {
|
|
value = Present_Value[object_instance];
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/* note: the object name must be unique within this device */
|
|
char *Binary_Value_Name(uint32_t object_instance)
|
|
{
|
|
static char text[16] = ""; /* okay for single thread */
|
|
|
|
if (object_instance < MAX_BINARY_VALUES) {
|
|
snprintf(text, sizeof(text), "BV-%lu", (unsigned long)object_instance);
|
|
return text;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* return apdu len, or -1 on error */
|
|
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
|
{
|
|
int len = 0;
|
|
int apdu_len = 0; /* return value */
|
|
BACNET_BIT_STRING bit_string;
|
|
BACNET_CHARACTER_STRING char_string;
|
|
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
|
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
|
unsigned object_index = 0;
|
|
unsigned i = 0;
|
|
bool state = false;
|
|
uint8_t *apdu = NULL;
|
|
|
|
Binary_Value_Initialize();
|
|
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
|
(rpdata->application_data_len == 0)) {
|
|
return 0;
|
|
}
|
|
apdu = rpdata->application_data;
|
|
switch (rpdata->object_property) {
|
|
case PROP_OBJECT_IDENTIFIER:
|
|
apdu_len = encode_application_object_id(
|
|
&apdu[0], OBJECT_BINARY_VALUE, rpdata->object_instance);
|
|
break;
|
|
/* note: Name and Description don't have to be the same.
|
|
You could make Description writable and different */
|
|
case PROP_OBJECT_NAME:
|
|
case PROP_DESCRIPTION:
|
|
characterstring_init_ansi(
|
|
&char_string, Binary_Value_Name(rpdata->object_instance));
|
|
apdu_len =
|
|
encode_application_character_string(&apdu[0], &char_string);
|
|
break;
|
|
case PROP_OBJECT_TYPE:
|
|
apdu_len =
|
|
encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
|
|
break;
|
|
case PROP_PRESENT_VALUE:
|
|
present_value = Binary_Value_Present_Value(rpdata->object_instance);
|
|
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
|
break;
|
|
case PROP_STATUS_FLAGS:
|
|
/* note: see the details in the standard on how to use these */
|
|
bitstring_init(&bit_string);
|
|
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
|
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
|
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
|
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
|
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
|
break;
|
|
case PROP_EVENT_STATE:
|
|
/* note: see the details in the standard on how to use this */
|
|
apdu_len =
|
|
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
|
break;
|
|
case PROP_OUT_OF_SERVICE:
|
|
apdu_len = encode_application_boolean(&apdu[0], false);
|
|
break;
|
|
case PROP_POLARITY:
|
|
/* FIXME: figure out the polarity */
|
|
apdu_len = encode_application_enumerated(&apdu[0], polarity);
|
|
break;
|
|
default:
|
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
apdu_len = -1;
|
|
break;
|
|
}
|
|
/* only array properties can have array options */
|
|
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
|
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
|
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
apdu_len = -1;
|
|
}
|
|
|
|
return apdu_len;
|
|
}
|
|
|
|
/* returns true if successful */
|
|
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
|
{
|
|
bool status = false; /* return value */
|
|
unsigned int object_index = 0;
|
|
unsigned int priority = 0;
|
|
BACNET_BINARY_PV level = BINARY_NULL;
|
|
int len = 0;
|
|
BACNET_APPLICATION_DATA_VALUE value;
|
|
|
|
if (!Binary_Value_Valid_Instance(wp_data->object_instance)) {
|
|
wp_data->error_class = ERROR_CLASS_OBJECT;
|
|
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
|
return false;
|
|
}
|
|
/* decode the some of the request */
|
|
len = bacapp_decode_application_data(
|
|
wp_data->application_data, wp_data->application_data_len, &value);
|
|
/* FIXME: len < application_data_len: more data? */
|
|
if (len < 0) {
|
|
/* error while decoding - a value larger than we can handle */
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
return false;
|
|
}
|
|
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
|
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
|
/* only array properties can have array options */
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
|
return false;
|
|
}
|
|
switch (wp_data->object_property) {
|
|
case PROP_PRESENT_VALUE:
|
|
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
|
priority = wp_data->priority;
|
|
/* Command priority 6 is reserved for use by Minimum On/Off
|
|
algorithm and may not be used for other purposes in any
|
|
object. */
|
|
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
|
(priority != 6 /* reserved */) &&
|
|
(value.type.Enumerated >= MIN_BINARY_PV) &&
|
|
(value.type.Enumerated <= MAX_BINARY_PV)) {
|
|
level = value.type.Enumerated;
|
|
object_index = Binary_Value_Instance_To_Index(
|
|
wp_data->object_instance);
|
|
priority--;
|
|
/* NOTE: this Binary value has no priority array */
|
|
Present_Value[object_index] = level;
|
|
/* Note: you could set the physical output here if we
|
|
are the highest priority.
|
|
However, if Out of Service is TRUE, then don't set the
|
|
physical output. */
|
|
status = true;
|
|
} else if (priority == 6) {
|
|
/* Command priority 6 is reserved for use by Minimum On/Off
|
|
algorithm and may not be used for other purposes in any
|
|
object. */
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
} else if (value.tag == BACNET_APPLICATION_TAG_NULL) {
|
|
#if 0
|
|
/* NOTE: this Binary Value has no priority array */
|
|
level = BINARY_NULL;
|
|
object_index =
|
|
Binary_Value_Instance_To_Index(wp_data->object_instance);
|
|
priority = wp_data->priority;
|
|
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
|
priority--;
|
|
Binary_Value_Level[object_index][priority] = level;
|
|
/* Note: you could set the physical output here to the next
|
|
highest priority, or to the relinquish default if no
|
|
priorities are set.
|
|
However, if Out of Service is TRUE, then don't set the
|
|
physical output. This comment may apply to the
|
|
main loop (i.e. check out of service before changing output) */
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
|
}
|
|
#else
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
#endif
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
case PROP_OUT_OF_SERVICE:
|
|
#if 0
|
|
if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) {
|
|
object_index =
|
|
Binary_Value_Instance_To_Index(wp_data->object_instance);
|
|
Binary_Value_Out_Of_Service[object_index] = value.type.Boolean;
|
|
status = true;
|
|
} else {
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
|
|
}
|
|
break;
|
|
#endif
|
|
case PROP_OBJECT_IDENTIFIER:
|
|
case PROP_OBJECT_NAME:
|
|
case PROP_OBJECT_TYPE:
|
|
case PROP_STATUS_FLAGS:
|
|
case PROP_EVENT_STATE:
|
|
case PROP_POLARITY:
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
|
break;
|
|
default:
|
|
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
|
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|