From 1302c1be7b46bc3742e2eb7a9cd1bdaa2441a518 Mon Sep 17 00:00:00 2001 From: skarg Date: Sun, 17 Jul 2016 19:23:19 +0000 Subject: [PATCH] =?UTF-8?q?Nikola=20Jeli=C4=87=20implemented=20the=20Acces?= =?UTF-8?q?s=20Objects=20for=20BACnet=20(Access=20Door,=20Access=20Point,?= =?UTF-8?q?=20Access=20Credential,=20Access=20Rights,=20Access=20User,=20A?= =?UTF-8?q?ccess=20Zone,=20and=20Credential=20Data=20Input).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bacnet-stack/demo/object/access_credential.c | 480 ++++++++++++ bacnet-stack/demo/object/access_credential.h | 124 ++++ .../demo/object/access_credential.mak | 45 ++ bacnet-stack/demo/object/access_door.c | 690 ++++++++++++++++++ bacnet-stack/demo/object/access_door.h | 143 ++++ bacnet-stack/demo/object/access_door.mak | 42 ++ bacnet-stack/demo/object/access_point.c | 476 ++++++++++++ bacnet-stack/demo/object/access_point.h | 120 +++ bacnet-stack/demo/object/access_point.mak | 43 ++ bacnet-stack/demo/object/access_rights.c | 435 +++++++++++ bacnet-stack/demo/object/access_rights.h | 110 +++ bacnet-stack/demo/object/access_rights.mak | 43 ++ bacnet-stack/demo/object/access_user.c | 373 ++++++++++ bacnet-stack/demo/object/access_user.h | 103 +++ bacnet-stack/demo/object/access_user.mak | 42 ++ bacnet-stack/demo/object/access_zone.c | 446 +++++++++++ bacnet-stack/demo/object/access_zone.h | 117 +++ bacnet-stack/demo/object/access_zone.mak | 42 ++ .../demo/object/credential_data_input.c | 462 ++++++++++++ .../demo/object/credential_data_input.h | 116 +++ .../demo/object/credential_data_input.mak | 45 ++ bacnet-stack/demo/server/Makefile | 7 + bacnet-stack/include/access_rule.h | 76 ++ bacnet-stack/include/assigned_access_rights.h | 62 ++ bacnet-stack/include/authentication_factor.h | 64 ++ .../include/authentication_factor_format.h | 60 ++ bacnet-stack/include/bacenum.h | 237 +++++- .../credential_authentication_factor.h | 63 ++ bacnet-stack/lib/Makefile | 5 + bacnet-stack/src/access_rule.c | 185 +++++ bacnet-stack/src/assigned_access_rights.c | 131 ++++ bacnet-stack/src/authentication_factor.c | 142 ++++ .../src/authentication_factor_format.c | 151 ++++ bacnet-stack/src/bactext.c | 2 +- .../src/credential_authentication_factor.c | 132 ++++ bacnet-stack/test.mak | 40 +- 36 files changed, 5850 insertions(+), 4 deletions(-) create mode 100644 bacnet-stack/demo/object/access_credential.c create mode 100644 bacnet-stack/demo/object/access_credential.h create mode 100644 bacnet-stack/demo/object/access_credential.mak create mode 100644 bacnet-stack/demo/object/access_door.c create mode 100644 bacnet-stack/demo/object/access_door.h create mode 100644 bacnet-stack/demo/object/access_door.mak create mode 100644 bacnet-stack/demo/object/access_point.c create mode 100644 bacnet-stack/demo/object/access_point.h create mode 100644 bacnet-stack/demo/object/access_point.mak create mode 100644 bacnet-stack/demo/object/access_rights.c create mode 100644 bacnet-stack/demo/object/access_rights.h create mode 100644 bacnet-stack/demo/object/access_rights.mak create mode 100644 bacnet-stack/demo/object/access_user.c create mode 100644 bacnet-stack/demo/object/access_user.h create mode 100644 bacnet-stack/demo/object/access_user.mak create mode 100644 bacnet-stack/demo/object/access_zone.c create mode 100644 bacnet-stack/demo/object/access_zone.h create mode 100644 bacnet-stack/demo/object/access_zone.mak create mode 100644 bacnet-stack/demo/object/credential_data_input.c create mode 100644 bacnet-stack/demo/object/credential_data_input.h create mode 100644 bacnet-stack/demo/object/credential_data_input.mak create mode 100644 bacnet-stack/include/access_rule.h create mode 100644 bacnet-stack/include/assigned_access_rights.h create mode 100644 bacnet-stack/include/authentication_factor.h create mode 100644 bacnet-stack/include/authentication_factor_format.h create mode 100644 bacnet-stack/include/credential_authentication_factor.h create mode 100644 bacnet-stack/src/access_rule.c create mode 100644 bacnet-stack/src/assigned_access_rights.c create mode 100644 bacnet-stack/src/authentication_factor.c create mode 100644 bacnet-stack/src/authentication_factor_format.c create mode 100644 bacnet-stack/src/credential_authentication_factor.c diff --git a/bacnet-stack/demo/object/access_credential.c b/bacnet-stack/demo/object/access_credential.c new file mode 100644 index 00000000..3b378eca --- /dev/null +++ b/bacnet-stack/demo/object/access_credential.c @@ -0,0 +1,480 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the credential to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access Credential Objects - customize for your use */ + +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_credential.h" +#include "handlers.h" + +static bool Access_Credential_Initialized = false; + +static ACCESS_CREDENTIAL_DESCR ac_descr[MAX_ACCESS_CREDENTIALS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_GLOBAL_IDENTIFIER, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_CREDENTIAL_STATUS, + PROP_REASON_FOR_DISABLE, + PROP_AUTHENTICATION_FACTORS, + PROP_ACTIVATION_TIME, + PROP_EXPIRATION_TIME, + PROP_CREDENTIAL_DISABLE, + PROP_ASSIGNED_ACCESS_RIGHTS, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_Credential_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_Credential_Init( + void) +{ + unsigned i; + + if (!Access_Credential_Initialized) { + Access_Credential_Initialized = true; + + for (i = 0; i < MAX_ACCESS_CREDENTIALS; i++) { + ac_descr[i].global_identifier = 0; /* set to some meaningful value */ + ac_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + ac_descr[i].credential_status = false; + ac_descr[i].reasons_count = 0; + ac_descr[i].auth_factors_count = 0; + memset(&ac_descr[i].activation_time, 0, sizeof(BACNET_DATE_TIME)); + memset(&ac_descr[i].expiration_time, 0, sizeof(BACNET_DATE_TIME)); + ac_descr[i].credential_disable = ACCESS_CREDENTIAL_DISABLE_NONE; + ac_descr[i].assigned_access_rights_count = 0; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_Credential_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_CREDENTIALS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_Credential_Count( + void) +{ + return MAX_ACCESS_CREDENTIALS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_Credential_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_Credential_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_CREDENTIALS; + + if (object_instance < MAX_ACCESS_CREDENTIALS) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Access_Credential_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_CREDENTIALS) { + sprintf(text_string, "ACCESS CREDENTIAL %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_Credential_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; + unsigned object_index = 0; + unsigned i = 0; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = + Access_Credential_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_ACCESS_CREDENTIAL, rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_Credential_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_ACCESS_CREDENTIAL); + break; + case PROP_GLOBAL_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + ac_descr[object_index].global_identifier); + break; + case PROP_STATUS_FLAGS: + 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_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + ac_descr[object_index].reliability); + break; + case PROP_CREDENTIAL_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + ac_descr[object_index].credential_status); + break; + case PROP_REASON_FOR_DISABLE: + for (i = 0; i < ac_descr[object_index].reasons_count; i++) { + len = + encode_application_enumerated(&apdu[0], + ac_descr[object_index].reason_for_disable[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + break; + case PROP_AUTHENTICATION_FACTORS: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + ac_descr[object_index].auth_factors_count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < ac_descr[object_index].auth_factors_count; i++) { + len = + bacapp_encode_credential_authentication_factor(&apdu + [0], &ac_descr[object_index].auth_factors[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= + ac_descr[object_index].auth_factors_count) { + apdu_len = + bacapp_encode_credential_authentication_factor(&apdu + [0], + &ac_descr[object_index]. + auth_factors[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_ACTIVATION_TIME: + apdu_len = + bacapp_encode_datetime(&apdu[0], + &ac_descr[object_index].activation_time); + break; + case PROP_EXPIRATION_TIME: + apdu_len = + bacapp_encode_datetime(&apdu[0], + &ac_descr[object_index].expiration_time); + break; + case PROP_CREDENTIAL_DISABLE: + apdu_len = + encode_application_enumerated(&apdu[0], + ac_descr[object_index].credential_disable); + break; + case PROP_ASSIGNED_ACCESS_RIGHTS: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + ac_descr[object_index].assigned_access_rights_count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; + i < ac_descr[object_index].assigned_access_rights_count; + i++) { + len = + bacapp_encode_assigned_access_rights(&apdu[0], + &ac_descr[object_index].assigned_access_rights[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= + ac_descr[object_index].assigned_access_rights_count) { + apdu_len = + bacapp_encode_assigned_access_rights(&apdu[0], + &ac_descr[object_index]. + assigned_access_rights[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && + (rpdata->object_property != PROP_AUTHENTICATION_FACTORS) + && (rpdata->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_Credential_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_AUTHENTICATION_FACTORS) + && (wp_data->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) + && (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = + Access_Credential_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_GLOBAL_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + ac_descr[object_index].global_identifier = + value.type.Unsigned_Int; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_CREDENTIAL_STATUS: + case PROP_REASON_FOR_DISABLE: + case PROP_AUTHENTICATION_FACTORS: + case PROP_ACTIVATION_TIME: + case PROP_EXPIRATION_TIME: + case PROP_CREDENTIAL_DISABLE: + case PROP_ASSIGNED_ACCESS_RIGHTS: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessCredential( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_Credential_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_CREDENTIAL; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_Credential_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_CREDENTIAL +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access Credential", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessCredential); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_CREDENTIAL */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_credential.h b/bacnet-stack/demo/object/access_credential.h new file mode 100644 index 00000000..f92fe2f0 --- /dev/null +++ b/bacnet-stack/demo/object/access_credential.h @@ -0,0 +1,124 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_CREDENTIAL_H +#define ACCESS_CREDENTIAL_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "datetime.h" +#include "timestamp.h" +#include "bacdevobjpropref.h" +#include "assigned_access_rights.h" +#include "credential_authentication_factor.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_CREDENTIALS +#define MAX_ACCESS_CREDENTIALS 4 +#endif + +#ifndef MAX_REASONS_FOR_DISABLE +#define MAX_REASONS_FOR_DISABLE 4 +#endif + +#ifndef MAX_AUTHENTICATION_FACTORS +#define MAX_AUTHENTICATION_FACTORS 4 +#endif + +#ifndef MAX_ASSIGNED_ACCESS_RIGHTS +#define MAX_ASSIGNED_ACCESS_RIGHTS 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + uint32_t global_identifier; + BACNET_RELIABILITY reliability; + bool credential_status; + uint32_t reasons_count; + BACNET_ACCESS_CREDENTIAL_DISABLE_REASON + reason_for_disable[MAX_REASONS_FOR_DISABLE]; + uint32_t auth_factors_count; + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR + auth_factors[MAX_AUTHENTICATION_FACTORS]; + BACNET_DATE_TIME activation_time, expiration_time; + BACNET_ACCESS_CREDENTIAL_DISABLE credential_disable; + uint32_t assigned_access_rights_count; + BACNET_ASSIGNED_ACCESS_RIGHTS + assigned_access_rights[MAX_ASSIGNED_ACCESS_RIGHTS]; + } ACCESS_CREDENTIAL_DESCR; + + void Access_Credential_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_Credential_Valid_Instance( + uint32_t object_instance); + unsigned Access_Credential_Count( + void); + uint32_t Access_Credential_Index_To_Instance( + unsigned index); + unsigned Access_Credential_Instance_To_Index( + uint32_t instance); + bool Access_Credential_Object_Instance_Add( + uint32_t instance); + + + bool Access_Credential_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_Credential_Name_Set( + uint32_t object_instance, + char *new_name); + + int Access_Credential_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_Credential_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_Credential_Create( + uint32_t object_instance); + bool Access_Credential_Delete( + uint32_t object_instance); + void Access_Credential_Cleanup( + void); + void Access_Credential_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessCredential( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_credential.mak b/bacnet-stack/demo/object/access_credential.mak new file mode 100644 index 00000000..ace9e266 --- /dev/null +++ b/bacnet-stack/demo/object/access_credential.mak @@ -0,0 +1,45 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_CREDENTIAL + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_credential.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/assigned_access_rights.c \ + $(SRC_DIR)/authentication_factor.c \ + $(SRC_DIR)/credential_authentication_factor.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_credential + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/access_door.c b/bacnet-stack/demo/object/access_door.c new file mode 100644 index 00000000..dd244d4f --- /dev/null +++ b/bacnet-stack/demo/object/access_door.c @@ -0,0 +1,690 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access Door Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_door.h" +#include "handlers.h" + +static bool Access_Door_Initialized = false; + +static ACCESS_DOOR_DESCR ad_descr[MAX_ACCESS_DOORS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_PRIORITY_ARRAY, + PROP_RELINQUISH_DEFAULT, + PROP_DOOR_PULSE_TIME, + PROP_DOOR_EXTENDED_PULSE_TIME, + PROP_DOOR_OPEN_TOO_LONG_TIME, + -1 +}; + +static const int Properties_Optional[] = { + PROP_DOOR_STATUS, + PROP_LOCK_STATUS, + PROP_SECURED_STATUS, + PROP_DOOR_UNLOCK_DELAY_TIME, + PROP_DOOR_ALARM_STATE, + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_Door_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_Door_Init( + void) +{ + unsigned i, j; + + if (!Access_Door_Initialized) { + Access_Door_Initialized = true; + + /* initialize all the access door priority arrays to NULL */ + for (i = 0; i < MAX_ACCESS_DOORS; i++) { + ad_descr[i].relinquish_default = DOOR_VALUE_LOCK; + ad_descr[i].event_state = EVENT_STATE_NORMAL; + ad_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + ad_descr[i].out_of_service = false; + ad_descr[i].door_status = DOOR_STATUS_CLOSED; + ad_descr[i].lock_status = LOCK_STATUS_LOCKED; + ad_descr[i].secured_status = DOOR_SECURED_STATUS_SECURED; + ad_descr[i].door_pulse_time = 30; /* 3s */ + ad_descr[i].door_extended_pulse_time = 50; /* 5s */ + ad_descr[i].door_unlock_delay_time = 0; /* 0s */ + ad_descr[i].door_open_too_long_time = 300; /* 30s */ + ad_descr[i].door_alarm_state = DOOR_ALARM_STATE_NORMAL; + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + ad_descr[i].value_active[j] = false; + /* just to fill in */ + ad_descr[i].priority_array[j] = DOOR_VALUE_LOCK; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_Door_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_DOORS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_Door_Count( + void) +{ + return MAX_ACCESS_DOORS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_Door_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_Door_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_DOORS; + + if (object_instance < MAX_ACCESS_DOORS) + index = object_instance; + + return index; +} + +BACNET_DOOR_VALUE Access_Door_Present_Value( + uint32_t object_instance) +{ + unsigned index = 0; + unsigned i = 0; + BACNET_DOOR_VALUE value = DOOR_VALUE_LOCK; + + index = Access_Door_Instance_To_Index(object_instance); + if (index < MAX_ACCESS_DOORS) { + value = ad_descr[i].relinquish_default; + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (ad_descr[index].value_active[i]) { + value = ad_descr[index].priority_array[i]; + break; + } + } + } + return value; +} + +unsigned Access_Door_Present_Value_Priority( + uint32_t object_instance) +{ + unsigned index = 0; /* instance to index conversion */ + unsigned i = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + + index = Access_Door_Instance_To_Index(object_instance); + if (index < MAX_ACCESS_DOORS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (ad_descr[index].value_active[i]) { + priority = i + 1; + break; + } + } + } + + return priority; +} + +bool Access_Door_Present_Value_Set( + uint32_t object_instance, + BACNET_DOOR_VALUE value, + unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Access_Door_Instance_To_Index(object_instance); + if (index < MAX_ACCESS_DOORS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= DOOR_VALUE_LOCK) && + (value <= DOOR_VALUE_EXTENDED_PULSE_UNLOCK)) { + ad_descr[index].value_active[priority - 1] = true; + ad_descr[index].priority_array[priority - 1] = value; + /* 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; + } + } + + return status; +} + +bool Access_Door_Present_Value_Relinquish( + uint32_t object_instance, + unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Access_Door_Instance_To_Index(object_instance); + if (index < MAX_ACCESS_DOORS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ )) { + ad_descr[index].value_active[priority - 1] = false; + /* 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; + } + } + + return status; +} + +BACNET_DOOR_VALUE Access_Door_Relinquish_Default( + uint32_t object_instance) +{ + BACNET_DOOR_VALUE status = -1; + unsigned index = 0; + index = Access_Door_Instance_To_Index(object_instance); + if (index < MAX_ACCESS_DOORS) { + return ad_descr[index].relinquish_default; + } + + return status; +} + +/* note: the object name must be unique within this device */ +bool Access_Door_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_DOORS) { + sprintf(text_string, "ACCESS DOOR %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +bool Access_Door_Out_Of_Service( + uint32_t instance) +{ + unsigned index = 0; + bool oos_flag = false; + + index = Access_Door_Instance_To_Index(instance); + if (index < MAX_ACCESS_DOORS) { + oos_flag = ad_descr[index].out_of_service; + } + + return oos_flag; +} + +void Access_Door_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag) +{ + unsigned index = 0; + + index = Access_Door_Instance_To_Index(instance); + if (index < MAX_ACCESS_DOORS) { + ad_descr[index].out_of_service = oos_flag; + } +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_Door_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; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Access_Door_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ACCESS_DOOR, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_Door_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ACCESS_DOOR); + break; + case PROP_PRESENT_VALUE: + apdu_len = + encode_application_enumerated(&apdu[0], + Access_Door_Present_Value(rpdata->object_instance)); + break; + case PROP_STATUS_FLAGS: + 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); + state = Access_Door_Out_Of_Service(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].event_state); + break; + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].reliability); + break; + case PROP_OUT_OF_SERVICE: + state = Access_Door_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = + encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (ad_descr[object_index].value_active[i]) + len = encode_application_null(&apdu[apdu_len]); + else + len = + encode_application_enumerated(&apdu[apdu_len], + ad_descr[object_index].priority_array[i]); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= BACNET_MAX_PRIORITY) { + if (ad_descr[object_index].value_active[i]) + apdu_len = encode_application_null(&apdu[0]); + else { + apdu_len = + encode_application_enumerated(&apdu[apdu_len], + ad_descr[object_index].priority_array[i]); + } + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_RELINQUISH_DEFAULT: + apdu_len = + encode_application_enumerated(&apdu[0], + Access_Door_Relinquish_Default(rpdata->object_instance)); + break; + case PROP_DOOR_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].door_status); + break; + case PROP_LOCK_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].lock_status); + break; + case PROP_SECURED_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].secured_status); + break; + case PROP_DOOR_PULSE_TIME: + apdu_len = + encode_application_unsigned(&apdu[0], + ad_descr[object_index].door_pulse_time); + break; + case PROP_DOOR_EXTENDED_PULSE_TIME: + apdu_len = + encode_application_unsigned(&apdu[0], + ad_descr[object_index].door_extended_pulse_time); + break; + case PROP_DOOR_UNLOCK_DELAY_TIME: + apdu_len = + encode_application_unsigned(&apdu[0], + ad_descr[object_index].door_unlock_delay_time); + break; + case PROP_DOOR_OPEN_TOO_LONG_TIME: + apdu_len = + encode_application_unsigned(&apdu[0], + ad_descr[object_index].door_open_too_long_time); + break; + case PROP_DOOR_ALARM_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + ad_descr[object_index].door_alarm_state); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_Door_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Access_Door_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = + Access_Door_Present_Value_Set(wp_data->object_instance, + value.type.Enumerated, wp_data->priority); + if (wp_data->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 if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + status = + Access_Door_Present_Value_Relinquish + (wp_data->object_instance, wp_data->priority); + if (!status) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + } + break; + case PROP_OUT_OF_SERVICE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, + &wp_data->error_class, &wp_data->error_code); + if (status) { + Access_Door_Out_Of_Service_Set(wp_data->object_instance, + value.type.Boolean); + } + break; + case PROP_DOOR_STATUS: + if (Access_Door_Out_Of_Service(wp_data->object_instance)) { + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, + &wp_data->error_code); + if (status) { + ad_descr[object_index].door_status = value.type.Enumerated; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_LOCK_STATUS: + if (Access_Door_Out_Of_Service(wp_data->object_instance)) { + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, + &wp_data->error_code); + if (status) { + ad_descr[object_index].lock_status = value.type.Enumerated; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_DOOR_ALARM_STATE: + if (Access_Door_Out_Of_Service(wp_data->object_instance)) { + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, + &wp_data->error_code); + if (status) { + ad_descr[object_index].door_alarm_state = + value.type.Enumerated; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_RELIABILITY: + case PROP_PRIORITY_ARRAY: + case PROP_RELINQUISH_DEFAULT: + case PROP_SECURED_STATUS: + case PROP_DOOR_PULSE_TIME: + case PROP_DOOR_EXTENDED_PULSE_TIME: + case PROP_DOOR_UNLOCK_DELAY_TIME: + case PROP_DOOR_OPEN_TOO_LONG_TIME: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessDoor( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_Door_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_DOOR; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_Door_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_DOOR +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access Door", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessDoor); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_DOOR */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_door.h b/bacnet-stack/demo/object/access_door.h new file mode 100644 index 00000000..329faf3c --- /dev/null +++ b/bacnet-stack/demo/object/access_door.h @@ -0,0 +1,143 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_DOOR_H +#define ACCESS_DOOR_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_DOORS +#define MAX_ACCESS_DOORS 4 +#endif + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + bool value_active[BACNET_MAX_PRIORITY]; + BACNET_DOOR_VALUE priority_array[BACNET_MAX_PRIORITY]; + BACNET_DOOR_VALUE relinquish_default; + BACNET_EVENT_STATE event_state; + BACNET_RELIABILITY reliability; + bool out_of_service; + BACNET_DOOR_STATUS door_status; + BACNET_LOCK_STATUS lock_status; + BACNET_DOOR_SECURED_STATUS secured_status; + uint32_t door_pulse_time, door_extended_pulse_time, + door_unlock_delay_time, door_open_too_long_time; + BACNET_DOOR_ALARM_STATE door_alarm_state; + } ACCESS_DOOR_DESCR; + + void Access_Door_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_Door_Valid_Instance( + uint32_t object_instance); + unsigned Access_Door_Count( + void); + uint32_t Access_Door_Index_To_Instance( + unsigned index); + unsigned Access_Door_Instance_To_Index( + uint32_t instance); + bool Access_Door_Object_Instance_Add( + uint32_t instance); + + BACNET_DOOR_VALUE Access_Door_Present_Value( + uint32_t object_instance); + unsigned Access_Door_Present_Value_Priority( + uint32_t object_instance); + bool Access_Door_Present_Value_Set( + uint32_t object_instance, + BACNET_DOOR_VALUE value, + unsigned priority); + bool Access_Door_Present_Value_Relinquish( + uint32_t object_instance, + unsigned priority); + + BACNET_DOOR_VALUE Access_Door_Relinquish_Default( + uint32_t object_instance); + bool Access_Door_Relinquish_Default_Set( + uint32_t object_instance, + float value); + + bool Access_Door_Change_Of_Value( + uint32_t instance); + void Access_Door_Change_Of_Value_Clear( + uint32_t instance); + bool Access_Door_Encode_Value_List( + uint32_t object_instance, + BACNET_PROPERTY_VALUE * value_list); + + bool Access_Door_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_Door_Name_Set( + uint32_t object_instance, + char *new_name); + + char *Access_Door_Description( + uint32_t instance); + bool Access_Door_Description_Set( + uint32_t instance, + char *new_name); + + bool Access_Door_Out_Of_Service( + uint32_t instance); + void Access_Door_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + int Access_Door_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_Door_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_Door_Create( + uint32_t object_instance); + bool Access_Door_Delete( + uint32_t object_instance); + void Access_Door_Cleanup( + void); + void Access_Door_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessDoor( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_door.mak b/bacnet-stack/demo/object/access_door.mak new file mode 100644 index 00000000..814aae25 --- /dev/null +++ b/bacnet-stack/demo/object/access_door.mak @@ -0,0 +1,42 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_DOOR + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_door.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_door + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/access_point.c b/bacnet-stack/demo/object/access_point.c new file mode 100644 index 00000000..dbcd9ac8 --- /dev/null +++ b/bacnet-stack/demo/object/access_point.c @@ -0,0 +1,476 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access Point Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_point.h" +#include "handlers.h" + +static bool Access_Point_Initialized = false; + +static ACCESS_POINT_DESCR ap_descr[MAX_ACCESS_POINTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_AUTHENTICATION_STATUS, + PROP_ACTIVE_AUTHENTICATION_POLICY, + PROP_NUMBER_OF_AUTHENTICATION_POLICIES, + PROP_AUTHORIZATION_MODE, + PROP_ACCESS_EVENT, + PROP_ACCESS_EVENT_TAG, + PROP_ACCESS_EVENT_TIME, + PROP_ACCESS_EVENT_CREDENTIAL, + PROP_ACCESS_DOORS, + PROP_PRIORITY_FOR_WRITING, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_Point_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_Point_Init( + void) +{ + unsigned i; + + if (!Access_Point_Initialized) { + Access_Point_Initialized = true; + + for (i = 0; i < MAX_ACCESS_POINTS; i++) { + ap_descr[i].event_state = EVENT_STATE_NORMAL; + ap_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + ap_descr[i].out_of_service = false; + ap_descr[i].authentication_status = + AUTHENTICATION_STATUS_NOT_READY; + ap_descr[i].active_authentication_policy = 0; + ap_descr[i].number_of_authentication_policies = 0; + ap_descr[i].authorization_mode = AUTHORIZATION_MODE_AUTHORIZE; + ap_descr[i].access_event = ACCESS_EVENT_NONE; + /* timestamp uninitialized */ + /* access_event_credential should be set to some meaningful value */ + ap_descr[i].num_doors = 0; + /* fill in the access doors with proper ids */ + ap_descr[i].priority_for_writing = 16; /* lowest possible for now */ + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_Point_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_POINTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_Point_Count( + void) +{ + return MAX_ACCESS_POINTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_Point_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_Point_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_POINTS; + + if (object_instance < MAX_ACCESS_POINTS) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Access_Point_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_POINTS) { + sprintf(text_string, "ACCESS POINT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +bool Access_Point_Out_Of_Service( + uint32_t instance) +{ + unsigned index = 0; + bool oos_flag = false; + + index = Access_Point_Instance_To_Index(instance); + if (index < MAX_ACCESS_POINTS) { + oos_flag = ap_descr[index].out_of_service; + } + + return oos_flag; +} + +void Access_Point_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag) +{ + unsigned index = 0; + + index = Access_Point_Instance_To_Index(instance); + if (index < MAX_ACCESS_POINTS) { + ap_descr[index].out_of_service = oos_flag; + } +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_Point_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; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Access_Point_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ACCESS_POINT, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_Point_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ACCESS_POINT); + break; + case PROP_STATUS_FLAGS: + 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); + state = Access_Point_Out_Of_Service(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + ap_descr[object_index].event_state); + break; + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + ap_descr[object_index].reliability); + break; + case PROP_OUT_OF_SERVICE: + state = Access_Point_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_AUTHENTICATION_STATUS: + apdu_len = + encode_application_enumerated(&apdu[0], + ap_descr[object_index].authentication_status); + break; + case PROP_ACTIVE_AUTHENTICATION_POLICY: + apdu_len = + encode_application_unsigned(&apdu[0], + ap_descr[object_index].active_authentication_policy); + break; + case PROP_NUMBER_OF_AUTHENTICATION_POLICIES: + apdu_len = + encode_application_unsigned(&apdu[0], + ap_descr[object_index].number_of_authentication_policies); + break; + case PROP_AUTHORIZATION_MODE: + apdu_len = + encode_application_enumerated(&apdu[0], + ap_descr[object_index].authorization_mode); + break; + case PROP_ACCESS_EVENT: + apdu_len = + encode_application_enumerated(&apdu[0], + ap_descr[object_index].access_event); + break; + case PROP_ACCESS_EVENT_TAG: + apdu_len = + encode_application_unsigned(&apdu[0], + ap_descr[object_index].access_event_tag); + break; + case PROP_ACCESS_EVENT_TIME: + apdu_len = + bacapp_encode_timestamp(&apdu[0], + &ap_descr[object_index].access_event_time); + break; + case PROP_ACCESS_EVENT_CREDENTIAL: + apdu_len = + bacapp_encode_device_obj_ref(&apdu[0], + &ap_descr[object_index].access_event_credential); + break; + case PROP_ACCESS_DOORS: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + ap_descr[object_index].num_doors); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < ap_descr[object_index].num_doors; i++) { + len = + bacapp_encode_device_obj_ref(&apdu[0], + &ap_descr[object_index].access_doors[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= ap_descr[object_index].num_doors) { + apdu_len = + bacapp_encode_device_obj_ref(&apdu[0], + &ap_descr[object_index].access_doors[rpdata-> + array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_ACCESS_DOORS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_Point_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_ACCESS_DOORS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + 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_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_RELIABILITY: + case PROP_OUT_OF_SERVICE: + case PROP_AUTHENTICATION_STATUS: + case PROP_ACTIVE_AUTHENTICATION_POLICY: + case PROP_NUMBER_OF_AUTHENTICATION_POLICIES: + case PROP_AUTHORIZATION_MODE: + case PROP_ACCESS_EVENT: + case PROP_ACCESS_EVENT_TAG: + case PROP_ACCESS_EVENT_TIME: + case PROP_ACCESS_EVENT_CREDENTIAL: + case PROP_ACCESS_DOORS: + case PROP_PRIORITY_FOR_WRITING: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessPoint( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_Point_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_POINT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_Point_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_POINT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access Point", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessPoint); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_POINT */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_point.h b/bacnet-stack/demo/object/access_point.h new file mode 100644 index 00000000..1ad9f916 --- /dev/null +++ b/bacnet-stack/demo/object/access_point.h @@ -0,0 +1,120 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_POINT_H +#define ACCESS_POINT_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "timestamp.h" +#include "bacdevobjpropref.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_POINTS +#define MAX_ACCESS_POINTS 4 +#endif + +#ifndef MAX_ACCESS_DOORS_COUNT +#define MAX_ACCESS_DOORS_COUNT 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + BACNET_EVENT_STATE event_state; + BACNET_RELIABILITY reliability; + bool out_of_service; + BACNET_AUTHENTICATION_STATUS authentication_status; + uint32_t active_authentication_policy, + number_of_authentication_policies; + BACNET_AUTHORIZATION_MODE authorization_mode; + BACNET_ACCESS_EVENT access_event; + uint32_t access_event_tag; + BACNET_TIMESTAMP access_event_time; + BACNET_DEVICE_OBJECT_REFERENCE access_event_credential; + uint32_t num_doors; /* helper value, not a property */ + BACNET_DEVICE_OBJECT_REFERENCE access_doors[MAX_ACCESS_DOORS_COUNT]; + uint8_t priority_for_writing; + } ACCESS_POINT_DESCR; + + void Access_Point_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_Point_Valid_Instance( + uint32_t object_instance); + unsigned Access_Point_Count( + void); + uint32_t Access_Point_Index_To_Instance( + unsigned index); + unsigned Access_Point_Instance_To_Index( + uint32_t instance); + bool Access_Point_Object_Instance_Add( + uint32_t instance); + + + bool Access_Point_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_Point_Name_Set( + uint32_t object_instance, + char *new_name); + + + bool Access_Point_Out_Of_Service( + uint32_t instance); + void Access_Point_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + int Access_Point_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_Point_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_Point_Create( + uint32_t object_instance); + bool Access_Point_Delete( + uint32_t object_instance); + void Access_Point_Cleanup( + void); + void Access_Point_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessPoint( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_point.mak b/bacnet-stack/demo/object/access_point.mak new file mode 100644 index 00000000..79ed5bba --- /dev/null +++ b/bacnet-stack/demo/object/access_point.mak @@ -0,0 +1,43 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_POINT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_point.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/timestamp.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_point + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/access_rights.c b/bacnet-stack/demo/object/access_rights.c new file mode 100644 index 00000000..563da43c --- /dev/null +++ b/bacnet-stack/demo/object/access_rights.c @@ -0,0 +1,435 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access Rights Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_rights.h" +#include "handlers.h" + +static bool Access_Rights_Initialized = false; + +static ACCESS_RIGHTS_DESCR ar_descr[MAX_ACCESS_RIGHTSS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_GLOBAL_IDENTIFIER, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_ENABLE, + PROP_NEGATIVE_ACCESS_RULES, + PROP_POSITIVE_ACCESS_RULES, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_Rights_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_Rights_Init( + void) +{ + unsigned i; + + if (!Access_Rights_Initialized) { + Access_Rights_Initialized = true; + + for (i = 0; i < MAX_ACCESS_RIGHTSS; i++) { + ar_descr[i].global_identifier = 0; /* set to some meaningful value */ + ar_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + ar_descr[i].enable = false; + ar_descr[i].negative_access_rules_count = 0; + ar_descr[i].positive_access_rules_count = 0; + /* fill in the positive and negative access rules with proper ids */ + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_Rights_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_RIGHTSS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_Rights_Count( + void) +{ + return MAX_ACCESS_RIGHTSS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_Rights_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_Rights_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_RIGHTSS; + + if (object_instance < MAX_ACCESS_RIGHTSS) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Access_Rights_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_RIGHTSS) { + sprintf(text_string, "ACCESS RIGHTS %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_Rights_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; + unsigned object_index = 0; + unsigned i = 0; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Access_Rights_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ACCESS_RIGHTS, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_Rights_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ACCESS_RIGHTS); + break; + case PROP_GLOBAL_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + ar_descr[object_index].global_identifier); + break; + case PROP_STATUS_FLAGS: + 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_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + ar_descr[object_index].reliability); + break; + case PROP_ENABLE: + apdu_len = + encode_application_boolean(&apdu[0], + ar_descr[object_index].enable); + break; + case PROP_NEGATIVE_ACCESS_RULES: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + ar_descr[object_index].negative_access_rules_count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; + i < ar_descr[object_index].negative_access_rules_count; + i++) { + len = + bacapp_encode_access_rule(&apdu[0], + &ar_descr[object_index].negative_access_rules[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= + ar_descr[object_index].negative_access_rules_count) { + apdu_len = + bacapp_encode_access_rule(&apdu[0], + &ar_descr[object_index]. + negative_access_rules[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + case PROP_POSITIVE_ACCESS_RULES: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + ar_descr[object_index].positive_access_rules_count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; + i < ar_descr[object_index].positive_access_rules_count; + i++) { + len = + bacapp_encode_access_rule(&apdu[0], + &ar_descr[object_index].positive_access_rules[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= + ar_descr[object_index].positive_access_rules_count) { + apdu_len = + bacapp_encode_access_rule(&apdu[0], + &ar_descr[object_index]. + positive_access_rules[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && + (rpdata->object_property != PROP_NEGATIVE_ACCESS_RULES) + && (rpdata->object_property != PROP_POSITIVE_ACCESS_RULES) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_Rights_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_NEGATIVE_ACCESS_RULES) + && (wp_data->object_property != PROP_POSITIVE_ACCESS_RULES) + && (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Access_Rights_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_GLOBAL_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + ar_descr[object_index].global_identifier = + value.type.Unsigned_Int; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_ENABLE: + case PROP_NEGATIVE_ACCESS_RULES: + case PROP_POSITIVE_ACCESS_RULES: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessRights( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_Rights_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_RIGHTS; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_Rights_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_RIGHTS +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access Rights", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessRights); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_RIGHTS */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_rights.h b/bacnet-stack/demo/object/access_rights.h new file mode 100644 index 00000000..b5491c87 --- /dev/null +++ b/bacnet-stack/demo/object/access_rights.h @@ -0,0 +1,110 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_RIGHTS_H +#define ACCESS_RIGHTS_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "bacdevobjpropref.h" +#include "access_rule.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_RIGHTSS +#define MAX_ACCESS_RIGHTSS 4 +#endif + +#ifndef MAX_NEGATIVE_ACCESS_RIGHTS_RULES +#define MAX_NEGATIVE_ACCESS_RIGHTS_RULES 4 +#endif + +#ifndef MAX_POSITIVE_ACCESS_RIGHTS_RULES +#define MAX_POSITIVE_ACCESS_RIGHTS_RULES 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + uint32_t global_identifier; + BACNET_RELIABILITY reliability; + bool enable; + uint32_t negative_access_rules_count, positive_access_rules_count; + BACNET_ACCESS_RULE + negative_access_rules[MAX_NEGATIVE_ACCESS_RIGHTS_RULES]; + BACNET_ACCESS_RULE + positive_access_rules[MAX_POSITIVE_ACCESS_RIGHTS_RULES]; + } ACCESS_RIGHTS_DESCR; + + void Access_Rights_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_Rights_Valid_Instance( + uint32_t object_instance); + unsigned Access_Rights_Count( + void); + uint32_t Access_Rights_Index_To_Instance( + unsigned index); + unsigned Access_Rights_Instance_To_Index( + uint32_t instance); + bool Access_Rights_Object_Instance_Add( + uint32_t instance); + + bool Access_Rights_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_Rights_Name_Set( + uint32_t object_instance, + char *new_name); + + int Access_Rights_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_Rights_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_Rights_Create( + uint32_t object_instance); + bool Access_Rights_Delete( + uint32_t object_instance); + void Access_Rights_Cleanup( + void); + void Access_Rights_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessRights( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_rights.mak b/bacnet-stack/demo/object/access_rights.mak new file mode 100644 index 00000000..7b28262b --- /dev/null +++ b/bacnet-stack/demo/object/access_rights.mak @@ -0,0 +1,43 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_RIGHTS + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_rights.c \ + $(SRC_DIR)/access_rule.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_rights + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/access_user.c b/bacnet-stack/demo/object/access_user.c new file mode 100644 index 00000000..9bd3452c --- /dev/null +++ b/bacnet-stack/demo/object/access_user.c @@ -0,0 +1,373 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access User Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_user.h" +#include "handlers.h" + +static bool Access_User_Initialized = false; + +static ACCESS_USER_DESCR au_descr[MAX_ACCESS_USERS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_GLOBAL_IDENTIFIER, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_USER_TYPE, + PROP_CREDENTIALS, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_User_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_User_Init( + void) +{ + unsigned i; + + if (!Access_User_Initialized) { + Access_User_Initialized = true; + + for (i = 0; i < MAX_ACCESS_USERS; i++) { + au_descr[i].global_identifier = 0; /* set to some meaningful value */ + au_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + au_descr[i].user_type = ACCESS_USER_TYPE_PERSON; + au_descr[i].credentials_count = 0; + /* fill in the credentials with proper ids */ + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_User_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_USERS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_User_Count( + void) +{ + return MAX_ACCESS_USERS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_User_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_User_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_USERS; + + if (object_instance < MAX_ACCESS_USERS) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Access_User_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_USERS) { + sprintf(text_string, "ACCESS USER %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_User_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; + unsigned object_index = 0; + unsigned i = 0; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Access_User_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ACCESS_USER, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_User_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ACCESS_USER); + break; + case PROP_GLOBAL_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + au_descr[object_index].global_identifier); + break; + case PROP_STATUS_FLAGS: + 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_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + au_descr[object_index].reliability); + break; + case PROP_USER_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + au_descr[object_index].user_type); + break; + case PROP_CREDENTIALS: + for (i = 0; i < au_descr[object_index].credentials_count; i++) { + len = + bacapp_encode_device_obj_ref(&apdu[0], + &au_descr[object_index].credentials[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + 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 = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_User_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Access_User_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_GLOBAL_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + au_descr[object_index].global_identifier = + value.type.Unsigned_Int; + } + break; + + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_RELIABILITY: + case PROP_USER_TYPE: + case PROP_CREDENTIALS: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessUser( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_User_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_USER; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_User_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_USER +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access User", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessUser); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_USER */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_user.h b/bacnet-stack/demo/object/access_user.h new file mode 100644 index 00000000..b3f334e6 --- /dev/null +++ b/bacnet-stack/demo/object/access_user.h @@ -0,0 +1,103 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_USER_H +#define ACCESS_USER_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "bacdevobjpropref.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_USERS +#define MAX_ACCESS_USERS 4 +#endif + +#ifndef MAX_ACCESS_USER_CREDENTIALS_COUNT +#define MAX_ACCESS_USER_CREDENTIALS_COUNT 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + uint32_t global_identifier; + BACNET_RELIABILITY reliability; + BACNET_ACCESS_USER_TYPE user_type; + uint32_t credentials_count; + BACNET_DEVICE_OBJECT_REFERENCE + credentials[MAX_ACCESS_USER_CREDENTIALS_COUNT]; + } ACCESS_USER_DESCR; + + void Access_User_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_User_Valid_Instance( + uint32_t object_instance); + unsigned Access_User_Count( + void); + uint32_t Access_User_Index_To_Instance( + unsigned index); + unsigned Access_User_Instance_To_Index( + uint32_t instance); + bool Access_User_Object_Instance_Add( + uint32_t instance); + + bool Access_User_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_User_Name_Set( + uint32_t object_instance, + char *new_name); + + int Access_User_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_User_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_User_Create( + uint32_t object_instance); + bool Access_User_Delete( + uint32_t object_instance); + void Access_User_Cleanup( + void); + void Access_User_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessUser( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_user.mak b/bacnet-stack/demo/object/access_user.mak new file mode 100644 index 00000000..12508fc8 --- /dev/null +++ b/bacnet-stack/demo/object/access_user.mak @@ -0,0 +1,42 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_USER + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_user.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_user + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/access_zone.c b/bacnet-stack/demo/object/access_zone.c new file mode 100644 index 00000000..e3350e46 --- /dev/null +++ b/bacnet-stack/demo/object/access_zone.c @@ -0,0 +1,446 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Access Zone Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "access_zone.h" +#include "handlers.h" + +static bool Access_Zone_Initialized = false; + +static ACCESS_ZONE_DESCR az_descr[MAX_ACCESS_ZONES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_GLOBAL_IDENTIFIER, + PROP_OCCUPANCY_STATE, + PROP_STATUS_FLAGS, + PROP_EVENT_STATE, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_ENTRY_POINTS, + PROP_EXIT_POINTS, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Access_Zone_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Access_Zone_Init( + void) +{ + unsigned i; + + if (!Access_Zone_Initialized) { + Access_Zone_Initialized = true; + + for (i = 0; i < MAX_ACCESS_ZONES; i++) { + az_descr[i].global_identifier = 0; /* set to some meaningful value */ + az_descr[i].occupancy_state = ACCESS_ZONE_OCCUPANCY_STATE_DISABLED; + az_descr[i].event_state = EVENT_STATE_NORMAL; + az_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + az_descr[i].out_of_service = false; + az_descr[i].entry_points_count = 0; + az_descr[i].exit_points_count = 0; + /* fill in the entry points and exit points with proper ids */ + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Access_Zone_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_ACCESS_ZONES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Access_Zone_Count( + void) +{ + return MAX_ACCESS_ZONES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Access_Zone_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Access_Zone_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_ACCESS_ZONES; + + if (object_instance < MAX_ACCESS_ZONES) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Access_Zone_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ACCESS_ZONES) { + sprintf(text_string, "ACCESS ZONE %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +bool Access_Zone_Out_Of_Service( + uint32_t instance) +{ + unsigned index = 0; + bool oos_flag = false; + + index = Access_Zone_Instance_To_Index(instance); + if (index < MAX_ACCESS_ZONES) { + oos_flag = az_descr[index].out_of_service; + } + + return oos_flag; +} + +void Access_Zone_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag) +{ + unsigned index = 0; + + index = Access_Zone_Instance_To_Index(instance); + if (index < MAX_ACCESS_ZONES) { + az_descr[index].out_of_service = oos_flag; + } +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Access_Zone_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; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = Access_Zone_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_ACCESS_ZONE, + rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Access_Zone_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_ACCESS_ZONE); + break; + case PROP_GLOBAL_IDENTIFIER: + apdu_len = + encode_application_unsigned(&apdu[0], + az_descr[object_index].global_identifier); + break; + case PROP_OCCUPANCY_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + az_descr[object_index].occupancy_state); + break; + case PROP_STATUS_FLAGS: + 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); + state = Access_Zone_Out_Of_Service(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + az_descr[object_index].event_state); + break; + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + az_descr[object_index].reliability); + break; + case PROP_OUT_OF_SERVICE: + state = Access_Zone_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_ENTRY_POINTS: + for (i = 0; i < az_descr[object_index].entry_points_count; i++) { + len = + bacapp_encode_device_obj_ref(&apdu[0], + &az_descr[object_index].entry_points[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + break; + case PROP_EXIT_POINTS: + for (i = 0; i < az_descr[object_index].exit_points_count; i++) { + len = + bacapp_encode_device_obj_ref(&apdu[0], + &az_descr[object_index].exit_points[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + 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 = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Access_Zone_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Access_Zone_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_GLOBAL_IDENTIFIER: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + if (status) { + az_descr[object_index].global_identifier = + value.type.Unsigned_Int; + } + break; + case PROP_RELIABILITY: + if (Access_Zone_Out_Of_Service(wp_data->object_instance)) { + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, + &wp_data->error_code); + if (status) { + az_descr[object_index].reliability = value.type.Enumerated; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_OCCUPANCY_STATE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_OUT_OF_SERVICE: + case PROP_ENTRY_POINTS: + case PROP_EXIT_POINTS: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testAccessZone( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Access_Zone_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_ACCESS_ZONE; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Access_Zone_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_ACCESS_ZONE +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Access Zone", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAccessZone); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ACCESS_ZONE */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/access_zone.h b/bacnet-stack/demo/object/access_zone.h new file mode 100644 index 00000000..88caf5e2 --- /dev/null +++ b/bacnet-stack/demo/object/access_zone.h @@ -0,0 +1,117 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef ACCESS_ZONE_H +#define ACCESS_ZONE_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "bacdevobjpropref.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_ACCESS_ZONES +#define MAX_ACCESS_ZONES 4 +#endif + +#ifndef MAX_ACCESS_ZONE_ENTRY_POINTS +#define MAX_ACCESS_ZONE_ENTRY_POINTS 4 +#endif + +#ifndef MAX_ACCESS_ZONE_EXIT_POINTS +#define MAX_ACCESS_ZONE_EXIT_POINTS 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + uint32_t global_identifier; + BACNET_ACCESS_ZONE_OCCUPANCY_STATE occupancy_state; + BACNET_EVENT_STATE event_state; + BACNET_RELIABILITY reliability; + bool out_of_service; + uint32_t entry_points_count, exit_points_count; + BACNET_DEVICE_OBJECT_REFERENCE + entry_points[MAX_ACCESS_ZONE_ENTRY_POINTS]; + BACNET_DEVICE_OBJECT_REFERENCE + exit_points[MAX_ACCESS_ZONE_EXIT_POINTS]; + } ACCESS_ZONE_DESCR; + + void Access_Zone_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Access_Zone_Valid_Instance( + uint32_t object_instance); + unsigned Access_Zone_Count( + void); + uint32_t Access_Zone_Index_To_Instance( + unsigned index); + unsigned Access_Zone_Instance_To_Index( + uint32_t instance); + bool Access_Zone_Object_Instance_Add( + uint32_t instance); + + bool Access_Zone_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Access_Zone_Name_Set( + uint32_t object_instance, + char *new_name); + + bool Access_Zone_Out_Of_Service( + uint32_t instance); + void Access_Zone_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + int Access_Zone_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Access_Zone_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Access_Zone_Create( + uint32_t object_instance); + bool Access_Zone_Delete( + uint32_t object_instance); + void Access_Zone_Cleanup( + void); + void Access_Zone_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testAccessZone( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/access_zone.mak b/bacnet-stack/demo/object/access_zone.mak new file mode 100644 index 00000000..1a9c0c94 --- /dev/null +++ b/bacnet-stack/demo/object/access_zone.mak @@ -0,0 +1,42 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_ZONE + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = access_zone.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(TEST_DIR)/ctest.c + +TARGET = access_zone + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/object/credential_data_input.c b/bacnet-stack/demo/object/credential_data_input.c new file mode 100644 index 00000000..87ad705d --- /dev/null +++ b/bacnet-stack/demo/object/credential_data_input.c @@ -0,0 +1,462 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Credential Data Input Objects - customize for your use */ + +#include +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" +#include "credential_data_input.h" +#include "handlers.h" + +static bool Credential_Data_Input_Initialized = false; + +static CREDENTIAL_DATA_INPUT_DESCR cdi_descr[MAX_CREDENTIAL_DATA_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_PRESENT_VALUE, + PROP_STATUS_FLAGS, + PROP_RELIABILITY, + PROP_OUT_OF_SERVICE, + PROP_SUPPORTED_FORMATS, + PROP_UPDATE_TIME, + -1 +}; + +static const int Properties_Optional[] = { + -1 +}; + +static const int Properties_Proprietary[] = { + -1 +}; + +void Credential_Data_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Properties_Required; + if (pOptional) + *pOptional = Properties_Optional; + if (pProprietary) + *pProprietary = Properties_Proprietary; + + return; +} + +void Credential_Data_Input_Init( + void) +{ + unsigned i; + + if (!Credential_Data_Input_Initialized) { + Credential_Data_Input_Initialized = true; + + for (i = 0; i < MAX_CREDENTIAL_DATA_INPUTS; i++) { + /* there should be a meaningful setup for present value */ + cdi_descr[i].present_value.format_type = + AUTHENTICATION_FACTOR_TYPE_UNDEFINED; + cdi_descr[i].present_value.format_class = 0; + octetstring_init(&cdi_descr[i].present_value.value, NULL, 0); + cdi_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED; + cdi_descr[i].out_of_service = false; + /* set supported formats */ + cdi_descr[i].supported_formats_count = 0; + /* timestamp uninitialized */ + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Credential_Data_Input_Valid_Instance( + uint32_t object_instance) +{ + if (object_instance < MAX_CREDENTIAL_DATA_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Credential_Data_Input_Count( + void) +{ + return MAX_CREDENTIAL_DATA_INPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Credential_Data_Input_Index_To_Instance( + unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Credential_Data_Input_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_CREDENTIAL_DATA_INPUTS; + + if (object_instance < MAX_CREDENTIAL_DATA_INPUTS) + index = object_instance; + + return index; +} + +/* note: the object name must be unique within this device */ +bool Credential_Data_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_CREDENTIAL_DATA_INPUTS) { + sprintf(text_string, "CREDENTIAL DATA INPUT %lu", + (unsigned long) object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +bool Credential_Data_Input_Out_Of_Service( + uint32_t instance) +{ + unsigned index = 0; + bool oos_flag = false; + + index = Credential_Data_Input_Instance_To_Index(instance); + if (index < MAX_CREDENTIAL_DATA_INPUTS) { + oos_flag = cdi_descr[index].out_of_service; + } + + return oos_flag; +} + +void Credential_Data_Input_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag) +{ + unsigned index = 0; + + index = Credential_Data_Input_Instance_To_Index(instance); + if (index < MAX_CREDENTIAL_DATA_INPUTS) { + cdi_descr[index].out_of_service = oos_flag; + } +} + +/* return apdu len, or BACNET_STATUS_ERROR on error */ +int Credential_Data_Input_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; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + apdu = rpdata->application_data; + object_index = + Credential_Data_Input_Instance_To_Index(rpdata->object_instance); + switch (rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], + OBJECT_CREDENTIAL_DATA_INPUT, rpdata->object_instance); + break; + case PROP_OBJECT_NAME: + Credential_Data_Input_Object_Name(rpdata->object_instance, + &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], + OBJECT_CREDENTIAL_DATA_INPUT); + break; + case PROP_PRESENT_VALUE: + apdu_len = + bacapp_encode_authentication_factor(&apdu[apdu_len], + &cdi_descr[object_index].present_value); + break; + case PROP_STATUS_FLAGS: + 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); + state = + Credential_Data_Input_Out_Of_Service(rpdata->object_instance); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state); + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], + cdi_descr[object_index].reliability); + break; + case PROP_OUT_OF_SERVICE: + state = + Credential_Data_Input_Out_Of_Service(rpdata->object_instance); + apdu_len = encode_application_boolean(&apdu[0], state); + break; + case PROP_SUPPORTED_FORMATS: + if (rpdata->array_index == 0) { + apdu_len = + encode_application_unsigned(&apdu[0], + cdi_descr[object_index].supported_formats_count); + } else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < cdi_descr[object_index].supported_formats_count; + i++) { + len = + bacapp_encode_authentication_factor_format(&apdu[0], + &cdi_descr[object_index].supported_formats[i]); + if (apdu_len + len < MAX_APDU) + apdu_len += len; + else { + rpdata->error_class = ERROR_CLASS_SERVICES; + rpdata->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = BACNET_STATUS_ERROR; + break; + } + } + } else { + if (rpdata->array_index <= + cdi_descr[object_index].supported_formats_count) { + apdu_len = + bacapp_encode_authentication_factor_format(&apdu[0], + &cdi_descr[object_index]. + supported_formats[rpdata->array_index - 1]); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + } + + break; + case PROP_UPDATE_TIME: + apdu_len = + bacapp_encode_timestamp(&apdu[0], + &cdi_descr[object_index].timestamp); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_SUPPORTED_FORMATS) + && (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Credential_Data_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + unsigned object_index = 0; + + /* 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; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_SUPPORTED_FORMATS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Credential_Data_Input_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (Credential_Data_Input_Out_Of_Service(wp_data->object_instance)) { + BACNET_AUTHENTICATION_FACTOR tmp; + len = + bacapp_decode_authentication_factor(wp_data-> + application_data, &tmp); + if (len > 0) { + memcpy(&cdi_descr[object_index].present_value, &tmp, + sizeof(BACNET_AUTHENTICATION_FACTOR)); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_RELIABILITY: + if (Credential_Data_Input_Out_Of_Service(wp_data->object_instance)) { + status = + WPValidateArgType(&value, + BACNET_APPLICATION_TAG_ENUMERATED, &wp_data->error_class, + &wp_data->error_code); + if (status) { + cdi_descr[object_index].reliability = + value.type.Enumerated; + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + break; + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_OUT_OF_SERVICE: + case PROP_SUPPORTED_FORMATS: + case PROP_UPDATE_TIME: + 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; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +bool WPValidateArgType( + BACNET_APPLICATION_DATA_VALUE * pValue, + uint8_t ucExpectedTag, + BACNET_ERROR_CLASS * pErrorClass, + BACNET_ERROR_CODE * pErrorCode) +{ + pValue = pValue; + ucExpectedTag = ucExpectedTag; + pErrorClass = pErrorClass; + pErrorCode = pErrorCode; + + return false; +} + +void testCredentialDataInput( + Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + uint32_t decoded_instance = 0; + uint16_t decoded_type = 0; + BACNET_READ_PROPERTY_DATA rpdata; + + Credential_Data_Input_Init(); + rpdata.application_data = &apdu[0]; + rpdata.application_data_len = sizeof(apdu); + rpdata.object_type = OBJECT_CREDENTIAL_DATA_INPUT; + rpdata.object_instance = 1; + rpdata.object_property = PROP_OBJECT_IDENTIFIER; + rpdata.array_index = BACNET_ARRAY_ALL; + len = Credential_Data_Input_Read_Property(&rpdata); + ct_test(pTest, len != 0); + len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID); + len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == rpdata.object_type); + ct_test(pTest, decoded_instance == rpdata.object_instance); + + return; +} + +#ifdef TEST_CREDENTIAL_DATA_INPUT +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Credential Data Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testCredentialDataInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_CREDENTIAL_DATA_INPUT */ +#endif /* TEST */ diff --git a/bacnet-stack/demo/object/credential_data_input.h b/bacnet-stack/demo/object/credential_data_input.h new file mode 100644 index 00000000..83fe423b --- /dev/null +++ b/bacnet-stack/demo/object/credential_data_input.h @@ -0,0 +1,116 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef CREDENTIAL_DATA_INPUT_H +#define CREDENTIAL_DATA_INPUT_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "timestamp.h" +#include "bacdevobjpropref.h" +#include "authentication_factor.h" +#include "authentication_factor_format.h" +#include "timestamp.h" +#include "rp.h" +#include "wp.h" + + +#ifndef MAX_CREDENTIAL_DATA_INPUTS +#define MAX_CREDENTIAL_DATA_INPUTS 4 +#endif + +#ifndef MAX_AUTHENTICATION_FACTOR_FORMAT_COUNT +#define MAX_AUTHENTICATION_FACTOR_FORMAT_COUNT 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + typedef struct { + BACNET_AUTHENTICATION_FACTOR present_value; + BACNET_RELIABILITY reliability; + bool out_of_service; + uint32_t supported_formats_count; + BACNET_AUTHENTICATION_FACTOR_FORMAT + supported_formats[MAX_AUTHENTICATION_FACTOR_FORMAT_COUNT]; + BACNET_TIMESTAMP timestamp; + } CREDENTIAL_DATA_INPUT_DESCR; + + void Credential_Data_Input_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + bool Credential_Data_Input_Valid_Instance( + uint32_t object_instance); + unsigned Credential_Data_Input_Count( + void); + uint32_t Credential_Data_Input_Index_To_Instance( + unsigned index); + unsigned Credential_Data_Input_Instance_To_Index( + uint32_t instance); + bool Credential_Data_Input_Object_Instance_Add( + uint32_t instance); + + + bool Credential_Data_Input_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING * object_name); + bool Credential_Data_Input_Name_Set( + uint32_t object_instance, + char *new_name); + + + bool Credential_Data_Input_Out_Of_Service( + uint32_t instance); + void Credential_Data_Input_Out_Of_Service_Set( + uint32_t instance, + bool oos_flag); + + int Credential_Data_Input_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + bool Credential_Data_Input_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + bool Credential_Data_Input_Create( + uint32_t object_instance); + bool Credential_Data_Input_Delete( + uint32_t object_instance); + void Credential_Data_Input_Cleanup( + void); + void Credential_Data_Input_Init( + void); + +#ifdef TEST +#include "ctest.h" + void testCredentialDataInput( + Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/demo/object/credential_data_input.mak b/bacnet-stack/demo/object/credential_data_input.mak new file mode 100644 index 00000000..776d5ff2 --- /dev/null +++ b/bacnet-stack/demo/object/credential_data_input.mak @@ -0,0 +1,45 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../../src +TEST_DIR = ../../test +INCLUDES = -I../../include -I$(TEST_DIR) -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_CREDENTIAL_DATA_INPUT + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = credential_data_input.c \ + $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/lighting.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/bacdevobjpropref.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/authentication_factor.c \ + $(SRC_DIR)/authentication_factor_format.c \ + $(SRC_DIR)/timestamp.c \ + $(TEST_DIR)/ctest.c + +TARGET = credential_data_input + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) + +include: .depend diff --git a/bacnet-stack/demo/server/Makefile b/bacnet-stack/demo/server/Makefile index 5ced91ce..0ba1b741 100644 --- a/bacnet-stack/demo/server/Makefile +++ b/bacnet-stack/demo/server/Makefile @@ -34,6 +34,13 @@ OBJECT_SRC = \ $(BACNET_OBJECT)/nc.c \ $(BACNET_OBJECT)/trendlog.c \ $(BACNET_OBJECT)/schedule.c \ + $(BACNET_OBJECT)/access_credential.c \ + $(BACNET_OBJECT)/access_door.c \ + $(BACNET_OBJECT)/access_point.c \ + $(BACNET_OBJECT)/access_rights.c \ + $(BACNET_OBJECT)/access_user.c \ + $(BACNET_OBJECT)/access_zone.c \ + $(BACNET_OBJECT)/credential_data_input.c \ $(BACNET_OBJECT)/bacfile.c SRCS = ${SRC} ${OBJECT_SRC} diff --git a/bacnet-stack/include/access_rule.h b/bacnet-stack/include/access_rule.h new file mode 100644 index 00000000..5514b83f --- /dev/null +++ b/bacnet-stack/include/access_rule.h @@ -0,0 +1,76 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef ACCESS_RULE_H +#define ACCESS_RULE_H + +#include +#include +#include "bacdef.h" +#include "bacapp.h" +#include "bacdevobjpropref.h" + +typedef enum { + TIME_RANGE_SPECIFIER_SPECIFIED = 0, + TIME_RANGE_SPECIFIER_ALWAYS = 1 +} BACNET_ACCESS_RULE_TIME_RANGE_SPECIFIER; + +typedef enum { + LOCATION_SPECIFIER_SPECIFIED = 0, + LOCATION_SPECIFIER_ALL = 1 +} BACNET_ACCESS_RULE_LOCATION_SPECIFIER; + +typedef struct { + BACNET_ACCESS_RULE_TIME_RANGE_SPECIFIER time_range_specifier; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE time_range; + BACNET_ACCESS_RULE_LOCATION_SPECIFIER location_specifier; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE location; + bool enable; +} BACNET_ACCESS_RULE; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_access_rule( + uint8_t * apdu, + BACNET_ACCESS_RULE * rule); + int bacapp_encode_context_access_rule( + uint8_t * apdu, + uint8_t tag_number, + BACNET_ACCESS_RULE * rule); + int bacapp_decode_access_rule( + uint8_t * apdu, + BACNET_ACCESS_RULE * rule); + int bacapp_decode_context_access_rule( + uint8_t * apdu, + uint8_t tag_number, + BACNET_ACCESS_RULE * rule); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/assigned_access_rights.h b/bacnet-stack/include/assigned_access_rights.h new file mode 100644 index 00000000..48a5223e --- /dev/null +++ b/bacnet-stack/include/assigned_access_rights.h @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_ASSIGNED_ACCESS_RIGHTS_H +#define BACNET_ASSIGNED_ACCESS_RIGHTS_H + +#include +#include +#include "bacdef.h" +#include "bacapp.h" +#include "bacdevobjpropref.h" + +typedef struct { + BACNET_DEVICE_OBJECT_REFERENCE assigned_access_rights; + bool enable; +} BACNET_ASSIGNED_ACCESS_RIGHTS; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_assigned_access_rights( + uint8_t * apdu, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar); + int bacapp_encode_context_assigned_access_rights( + uint8_t * apdu, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar); + int bacapp_decode_assigned_access_rights( + uint8_t * apdu, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar); + int bacapp_decode_context_assigned_access_rights( + uint8_t * apdu, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/authentication_factor.h b/bacnet-stack/include/authentication_factor.h new file mode 100644 index 00000000..120d7ada --- /dev/null +++ b/bacnet-stack/include/authentication_factor.h @@ -0,0 +1,64 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_AUTHENTICATION_FACTOR_H +#define BACNET_AUTHENTICATION_FACTOR_H + +#include +#include +#include "bacdef.h" +#include "bacapp.h" + +typedef struct { + BACNET_AUTHENTICATION_FACTOR_TYPE format_type; + uint32_t format_class; + BACNET_OCTET_STRING value; +} BACNET_AUTHENTICATION_FACTOR; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_authentication_factor( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR * af); + int bacapp_encode_context_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR * af); + int bacapp_decode_authentication_factor( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR * af); + int bacapp_decode_context_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR * af); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif + diff --git a/bacnet-stack/include/authentication_factor_format.h b/bacnet-stack/include/authentication_factor_format.h new file mode 100644 index 00000000..9b18c8ed --- /dev/null +++ b/bacnet-stack/include/authentication_factor_format.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_AUTHENTICATION_FACTOR_FORMAT_H +#define BACNET_AUTHENTICATION_FACTOR_FORMAT_H + +#include +#include +#include "bacdef.h" + +typedef struct { + BACNET_AUTHENTICATION_FACTOR_TYPE format_type; + uint32_t vendor_id, vendor_format; +} BACNET_AUTHENTICATION_FACTOR_FORMAT; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_authentication_factor_format( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff); + int bacapp_encode_context_authentication_factor_format( + uint8_t * apdu, + uint8_t tag_number, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff); + int bacapp_decode_authentication_factor_format( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff); + int bacapp_decode_context_authentication_factor_format( + uint8_t * apdu, + uint8_t tag_number, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index 08e05732..3f460629 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -290,7 +290,7 @@ typedef enum { PROP_DAYS_REMAINING = 267, PROP_ENTRY_POINTS = 268, PROP_EXIT_POINTS = 269, - PROP_EXPIRY_TIME = 270, + PROP_EXPIRATION_TIME = 270, PROP_EXTENDED_TIME_ENABLE = 271, PROP_FAILED_ATTEMPT_EVENTS = 272, PROP_FAILED_ATTEMPTS = 273, @@ -876,13 +876,23 @@ typedef enum { EVENT_EXTENDED = 9, EVENT_BUFFER_READY = 10, EVENT_UNSIGNED_RANGE = 11, + EVENT_ACCESS_EVENT = 13, + EVENT_DOUBLE_OUT_OF_RANGE = 14, + EVENT_SIGNED_OUT_OF_RANGE = 15, + EVENT_UNSIGNED_OUT_OF_RANGE = 16, + EVENT_CHANGE_OF_CHARACTERSTRING = 17, + EVENT_CHANGE_OF_STATUS_FLAGS = 18, + EVENT_CHANGE_OF_RELIABILITY = 19, + EVENT_NONE = 20, + EVENT_CHANGE_OF_DISCRETE_VALUE = 21, + EVENT_CHANGE_OF_TIMER = 22, /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ /* Enumerated values 64-65535 may be used by others subject to */ /* the procedures and constraints described in Clause 23. */ /* It is expected that these enumerated values will correspond to */ /* the use of the complex-event-type CHOICE [6] of the */ /* BACnetNotificationParameters production. */ - /* The last enumeration used in this version is 11. */ + /* The last enumeration used in this version is 22. */ /* do the max range inside of enum so that compilers will allocate adequate sized datatype for enum which is used to store decoding */ @@ -1783,6 +1793,229 @@ typedef enum BACnetEventTransitionMask { TRANSITION_TO_NORMAL_MASKED = 4 } BACNET_EVENT_TRANSITION_MASK; +/* Access Enumerations */ +/* Door Alarm State */ +typedef enum { + DOOR_ALARM_STATE_NORMAL = 0, + DOOR_ALARM_STATE_ALARM = 1, + DOOR_ALARM_STATE_DOOR_OPEN_TOO_LONG = 2, + DOOR_ALARM_STATE_FORCED_OPEN = 3, + DOOR_ALARM_STATE_TAMPER = 4, + DOOR_ALARM_STATE_DOOR_FAULT = 5, + DOOR_ALARM_STATE_LOCK_DOWN = 6, + DOOR_ALARM_STATE_FREE_ACCESS = 7, + DOOR_ALARM_STATE_EGRESS_OPEN = 8 +} BACNET_DOOR_ALARM_STATE; + +/* Door Secured Status */ +typedef enum { + DOOR_SECURED_STATUS_SECURED = 0, + DOOR_SECURED_STATUS_UNSECURED = 1, + DOOR_SECURED_STATUS_UNKNOWN = 2 +} BACNET_DOOR_SECURED_STATUS; + +/* Door Status */ +typedef enum { + DOOR_STATUS_CLOSED = 0, + DOOR_STATUS_OPENED = 1, + DOOR_STATUS_UNKNOWN = 2, + DOOR_STATUS_DOOR_FAULT = 4, + DOOR_STATUS_UNUSED = 5 +} BACNET_DOOR_STATUS; + +/* Door Value */ +typedef enum { + DOOR_VALUE_LOCK = 0, + DOOR_VALUE_UNLOCK = 1, + DOOR_VALUE_PULSE_UNLOCK = 2, + DOOR_VALUE_EXTENDED_PULSE_UNLOCK = 3 +} BACNET_DOOR_VALUE; + +/* Lock Status */ +typedef enum { + LOCK_STATUS_LOCKED = 0, + LOCK_STATUS_UNLOCKED = 1, + LOCK_STATUS_LOCK_FAULT = 2, + LOCK_STATUS_UNUSED = 3, + LOCK_STATUS_UNKNOWN = 4 +} BACNET_LOCK_STATUS; + +/* Access Event */ +typedef enum { + ACCESS_EVENT_NONE = 0, + ACCESS_EVENT_GRANTED = 1, + ACCESS_EVENT_MUSTER = 2, + ACCESS_EVENT_PASSBACK_DETECTED = 3, + ACCESS_EVENT_DURESS = 4, + ACCESS_EVENT_TRACE = 5, + ACCESS_EVENT_LOCKOUT_MAX_ATTEMPTS = 6, + ACCESS_EVENT_LOCKOUT_OTHER = 7, + ACCESS_EVENT_LOCKOUT_RELINQUISHED = 8, + ACCESS_EVENT_LOCKED_BY_HIGHER_AUTHORITY = 9, + ACCESS_EVENT_OUT_OF_SERVICE = 10, + ACCESS_EVENT_OUT_OF_SERVICE_RELINQUISHED = 11, + ACCESS_EVENT_ACCOMPANIMENT_BY = 12, + ACCESS_EVENT_AUTHENTICATION_FACTOR_READ = 13, + ACCESS_EVENT_AUTHORIZATION_DELAYED = 14, + ACCESS_EVENT_VERIFICATION_REQUIRED = 15, + /* values over 128 indicate that access is denied */ + ACCESS_EVENT_DENIED_DENY_ALL = 128, + ACCESS_EVENT_DENIED_UNKNOWN_CREDENTIAL = 129, + ACCESS_EVENT_DENIED_AUTHENTICATION_UNAVAILABLE = 130, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_TIMEOUT = 131, + ACCESS_EVENT_DENIED_INCORRECT_AUTHENTICATION_FACTOR = 132, + ACCESS_EVENT_DENIED_ZONE_NO_ACCESS_RIGHTS = 133, + ACCESS_EVENT_DENIED_POINT_NO_ACCESS_RIGHTS = 134, + ACCESS_EVENT_DENIED_NO_ACCESS_RIGHTS = 135, + ACCESS_EVENT_DENIED_OUT_OF_TIME_RANGE = 136, + ACCESS_EVENT_DENIED_THREAT_LEVEL = 137, + ACCESS_EVENT_DENIED_PASSBACK = 138, + ACCESS_EVENT_DENIED_UNEXPECTED_LOCATION_USAGE = 139, + ACCESS_EVENT_DENIED_MAX_ATTEMPTS = 140, + ACCESS_EVENT_DENIED_LOWER_OCCUPANCY_LIMIT = 141, + ACCESS_EVENT_DENIED_UPPER_OCCUPANCY_LIMIT = 142, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_LOST = 143, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_STOLEN = 144, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_DAMAGED = 145, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_DESTROYED = 146, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_DISABLED = 147, + ACCESS_EVENT_DENIED_AUTHENTICATION_FACTOR_ERROR = 148, + ACCESS_EVENT_DENIED_CREDENTIAL_UNASSIGNED = 149, + ACCESS_EVENT_DENIED_CREDENTIAL_NOT_PROVISIONED = 150, + ACCESS_EVENT_DENIED_CREDENTIAL_NOT_YET_ACTIVE = 151, + ACCESS_EVENT_DENIED_CREDENTIAL_EXPIRED = 152, + ACCESS_EVENT_DENIED_CREDENTIAL_MANUAL_DISABLE = 153, + ACCESS_EVENT_DENIED_CREDENTIAL_LOCKOUT = 154, + ACCESS_EVENT_DENIED_CREDENTIAL_MAX_DAYS = 155, + ACCESS_EVENT_DENIED_CREDENTIAL_MAX_USES = 156, + ACCESS_EVENT_DENIED_CREDENTIAL_INACTIVITY = 157, + ACCESS_EVENT_DENIED_CREDENTIAL_DISABLED = 158, + ACCESS_EVENT_DENIED_NO_ACCOMPANIMENT = 159, + ACCESS_EVENT_DENIED_INCORRECT_ACCOMPANIMENT = 160, + ACCESS_EVENT_DENIED_LOCKOUT = 161, + ACCESS_EVENT_DENIED_VERIFICATION_FAILED = 162, + ACCESS_EVENT_DENIED_VERIFICATION_TIMEOUT = 163, + ACCESS_EVENT_DENIED_OTHER = 164 +} BACNET_ACCESS_EVENT; + +/* Authentication Status */ +typedef enum { + AUTHENTICATION_STATUS_NOT_READY = 0, + AUTHENTICATION_STATUS_READY = 1, + AUTHENTICATION_STATUS_DISABLED = 2, + AUTHENTICATION_STATUS_WAITING_FOR_AUTHENTICATION_FACTOR = 3, + AUTHENTICATION_STATUS_WAITING_FOR_ACCOMPANIMENT = 4, + AUTHENTICATION_STATUS_WAITING_FOR_VERIFICATION = 5, + AUTHENTICATION_STATUS_IN_PROGRESS = 6 +} BACNET_AUTHENTICATION_STATUS; + +/* Authorization Mode */ +typedef enum { + AUTHORIZATION_MODE_AUTHORIZE = 0, + AUTHORIZATION_MODE_GRANT_ACTIVE = 1, + AUTHORIZATION_MODE_DENY_ALL = 2, + AUTHORIZATION_MODE_VERIFICATION_REQUIRED = 3, + AUTHORIZATION_MODE_AUTHORIZATION_DELAYED = 4, + AUTHORIZATION_MODE_NONE = 5 +} BACNET_AUTHORIZATION_MODE; + +/* Access Passback Mode */ +typedef enum { + ACCESS_PASSBACK_MODE_PASSBACK_OFF = 0, + ACCESS_PASSBACK_MODE_HARD_PASSBACK = 1, + ACCESS_PASSBACK_MODE_SOFT_PASSBACK = 2 +} BACNET_ACCESS_PASSBACK_MODE; + +/* Access Zone Occupancy State */ +typedef enum { + ACCESS_ZONE_OCCUPANCY_STATE_NORMAL = 0, + ACCESS_ZONE_OCCUPANCY_STATE_BELOW_LOWER_LIMIT = 1, + ACCESS_ZONE_OCCUPANCY_STATE_AT_LOWER_LIMIT = 2, + ACCESS_ZONE_OCCUPANCY_STATE_AT_UPPER_LIMIT = 3, + ACCESS_ZONE_OCCUPANCY_STATE_ABOVE_UPPER_LIMIT = 4, + ACCESS_ZONE_OCCUPANCY_STATE_DISABLED = 5, + ACCESS_ZONE_OCCUPANCY_STATE_NOT_SUPPORTED = 6 +} BACNET_ACCESS_ZONE_OCCUPANCY_STATE; + +/* Access User Type */ +typedef enum { + ACCESS_USER_TYPE_ASSET = 0, + ACCESS_USER_TYPE_GROUP = 1, + ACCESS_USER_TYPE_PERSON = 2 +} BACNET_ACCESS_USER_TYPE; + +/* Access Authentication Factor Disable */ +typedef enum { + ACCESS_AUTHENTICATION_FACTOR_DISABLE_NONE = 0, + ACCESS_AUTHENTICATION_FACTOR_DISABLE_DISABLED = 1, + ACCESS_AUTHENTICATION_FACTOR_DISABLE_DISABLED_LOST = 2, + ACCESS_AUTHENTICATION_FACTOR_DISABLE_DISABLED_STOLEN = 3, + ACCESS_AUTHENTICATION_FACTOR_DISABLE_DISABLED_DAMAGED = 4, + ACCESS_AUTHENTICATION_FACTOR_DISABLE_DISABLED_DESTROYED = 5 +} BACNET_ACCESS_AUTHENTICATION_FACTOR_DISABLE; + +/* Access Credential Disable */ +typedef enum { + ACCESS_CREDENTIAL_DISABLE_NONE = 0, + ACCESS_CREDENTIAL_DISABLE_DISABLE = 1, + ACCESS_CREDENTIAL_DISABLE_DISABLE_MANUAL = 2, + ACCESS_CREDENTIAL_DISABLE_DISABLE_LOCKOUT = 3 +} BACNET_ACCESS_CREDENTIAL_DISABLE; + +/* Access Credential Disable Reason */ +typedef enum { + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED = 0, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_NEEDS_PROVISIONING = 1, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_UNASSIGNED = 2, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_NOT_YET_ACTIVE = 3, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_EXPIRED = 4, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_LOCKOUT = 5, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_MAX_DAYS = 6, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_MAX_USES = 7, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_INACTIVITY = 8, + ACCESS_CREDENTIAL_DISABLE_REASON_DISABLED_MANUAL = 9 +} BACNET_ACCESS_CREDENTIAL_DISABLE_REASON; + +/* Authentication Factor Type */ +typedef enum { + AUTHENTICATION_FACTOR_TYPE_UNDEFINED = 0, + AUTHENTICATION_FACTOR_TYPE_ERROR = 1, + AUTHENTICATION_FACTOR_TYPE_CUSTOM = 2, + AUTHENTICATION_FACTOR_TYPE_SIMPLE_NUMBER16 = 3, + AUTHENTICATION_FACTOR_TYPE_SIMPLE_NUMBER32 = 4, + AUTHENTICATION_FACTOR_TYPE_SIMPLE_NUMBER56 = 5, + AUTHENTICATION_FACTOR_TYPE_SIMPLE_ALPHA_NUMERIC = 6, + AUTHENTICATION_FACTOR_TYPE_ABA_TRACK2 = 7, + AUTHENTICATION_FACTOR_TYPE_WIEGAND26 = 8, + AUTHENTICATION_FACTOR_TYPE_WIEGAND37 = 9, + AUTHENTICATION_FACTOR_TYPE_WIEGAND37_FACILITY = 10, + AUTHENTICATION_FACTOR_TYPE_FACILITY16_CARD32 = 11, + AUTHENTICATION_FACTOR_TYPE_FACILITY32_CARD32 = 12, + AUTHENTICATION_FACTOR_TYPE_FASC_N = 13, + AUTHENTICATION_FACTOR_TYPE_FASC_N_BCD = 14, + AUTHENTICATION_FACTOR_TYPE_FASC_N_LARGE = 15, + AUTHENTICATION_FACTOR_TYPE_FASC_N_LARGE_BCD = 16, + AUTHENTICATION_FACTOR_TYPE_GSA75 = 17, + AUTHENTICATION_FACTOR_TYPE_CHUID = 18, + AUTHENTICATION_FACTOR_TYPE_CHUID_FULL = 19, + AUTHENTICATION_FACTOR_TYPE_GUID = 20, + AUTHENTICATION_FACTOR_TYPE_CBEFF_A = 21, + AUTHENTICATION_FACTOR_TYPE_CBEFF_B = 22, + AUTHENTICATION_FACTOR_TYPE_CBEFF_C = 23, + AUTHENTICATION_FACTOR_TYPE_USER_PASSWORD = 24 +} BACNET_AUTHENTICATION_FACTOR_TYPE; + +/* Authorization Exemption */ +typedef enum { + AUTHORIZATION_EXEMPTION_PASSBACK = 0, + AUTHORIZATION_EXEMPTION_OCCUPANCY_CHECK = 1, + AUTHORIZATION_EXEMPTION_ACCESS_RIGHTS = 2, + AUTHORIZATION_EXEMPTION_LOCKOUT = 3, + AUTHORIZATION_EXEMPTION_DENY = 4, + AUTHORIZATION_EXEMPTION_VERIFICATION = 5, + AUTHORIZATION_EXEMPTION_AUTHORIZATION_DELAY = 6 +} BACNET_AUTHORIZATION_EXEMPTION; + /* The Network Reject Reasons for NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK */ typedef enum { NETWORK_REJECT_UNKNOWN_ERROR = 0, diff --git a/bacnet-stack/include/credential_authentication_factor.h b/bacnet-stack/include/credential_authentication_factor.h new file mode 100644 index 00000000..ca4e7069 --- /dev/null +++ b/bacnet-stack/include/credential_authentication_factor.h @@ -0,0 +1,63 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_CREDENTIAL_AUTHENTICATION_FACTOR_H +#define BACNET_CREDENTIAL_AUTHENTICATION_FACTOR_H + +#include +#include +#include "bacdef.h" +#include "bacapp.h" +#include "authentication_factor.h" + +typedef struct { + BACNET_ACCESS_AUTHENTICATION_FACTOR_DISABLE disable; + BACNET_AUTHENTICATION_FACTOR authentication_factor; +} BACNET_CREDENTIAL_AUTHENTICATION_FACTOR; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int bacapp_encode_credential_authentication_factor( + uint8_t * apdu, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf); + int bacapp_encode_context_credential_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf); + int bacapp_decode_credential_authentication_factor( + uint8_t * apdu, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf); + int bacapp_decode_context_credential_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index 020fc40d..503d7b1f 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -75,6 +75,11 @@ CORE_SRC = \ $(BACNET_CORE)/timestamp.c \ $(BACNET_CORE)/lighting.c \ $(BACNET_CORE)/bacsec.c \ + $(BACNET_CORE)/access_rule.c \ + $(BACNET_CORE)/assigned_access_rights.c \ + $(BACNET_CORE)/authentication_factor_format.c \ + $(BACNET_CORE)/authentication_factor.c \ + $(BACNET_CORE)/credential_authentication_factor.c \ $(BACNET_CORE)/version.c HANDLER_SRC = \ diff --git a/bacnet-stack/src/access_rule.c b/bacnet-stack/src/access_rule.c new file mode 100644 index 00000000..d4adebc7 --- /dev/null +++ b/bacnet-stack/src/access_rule.c @@ -0,0 +1,185 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include +#include +#include "access_rule.h" +#include "bacdcode.h" + +int bacapp_encode_access_rule( + uint8_t * apdu, + BACNET_ACCESS_RULE * rule) +{ + int len; + int apdu_len = 0; + + len = encode_context_enumerated(&apdu[0], 0, rule->time_range_specifier); + apdu_len += len; + + if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) { + len = + bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 1, + &rule->time_range); + if (len > 0) + apdu_len += len; + else + return -1; + } + + len = + encode_context_enumerated(&apdu[apdu_len], 2, + rule->location_specifier); + apdu_len += len; + + if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) { + len = + bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 3, + &rule->location); + if (len > 0) + apdu_len += len; + else + return -1; + } + + len = encode_context_boolean(&apdu[apdu_len], 4, rule->enable); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_access_rule( + uint8_t * apdu, + uint8_t tag_number, + BACNET_ACCESS_RULE * rule) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_access_rule(&apdu[apdu_len], rule); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_access_rule( + uint8_t * apdu, + BACNET_ACCESS_RULE * rule) +{ + int len; + int apdu_len = 0; + + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + len = + decode_context_enumerated(&apdu[apdu_len], 0, + &rule->time_range_specifier); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) { + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = + bacapp_decode_context_device_obj_property_ref(&apdu[apdu_len], + 1, &rule->time_range); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + } + + if (decode_is_context_tag(&apdu[apdu_len], 2)) { + len = + decode_context_enumerated(&apdu[apdu_len], 2, + &rule->location_specifier); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) { + if (decode_is_context_tag(&apdu[apdu_len], 3)) { + len = + bacapp_decode_context_device_obj_property_ref(&apdu[apdu_len], + 3, &rule->location); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + } + + if (decode_is_context_tag(&apdu[apdu_len], 4)) { + len = decode_context_boolean2(&apdu[apdu_len], 4, &rule->enable); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + return apdu_len; +} + +int bacapp_decode_context_access_rule( + uint8_t * apdu, + uint8_t tag_number, + BACNET_ACCESS_RULE * rule) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = bacapp_decode_access_rule(&apdu[len], rule); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} diff --git a/bacnet-stack/src/assigned_access_rights.c b/bacnet-stack/src/assigned_access_rights.c new file mode 100644 index 00000000..81f984f5 --- /dev/null +++ b/bacnet-stack/src/assigned_access_rights.c @@ -0,0 +1,131 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include "assigned_access_rights.h" +#include "bacdcode.h" + + +int bacapp_encode_assigned_access_rights( + uint8_t * apdu, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar) +{ + int len; + int apdu_len = 0; + + len = + bacapp_encode_context_device_obj_ref(&apdu[apdu_len], 0, + &aar->assigned_access_rights); + if (len < 0) + return -1; + else + apdu_len += len; + + len = encode_context_boolean(&apdu[apdu_len], 1, aar->enable); + if (len < 0) + return -1; + else + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_assigned_access_rights( + uint8_t * apdu, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag); + apdu_len += len; + + len = bacapp_encode_assigned_access_rights(&apdu[apdu_len], aar); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag); + apdu_len += len; + + return apdu_len; + +} + +int bacapp_decode_assigned_access_rights( + uint8_t * apdu, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar) +{ + int len; + int apdu_len = 0; + + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + len = + bacapp_decode_context_device_obj_ref(&apdu[apdu_len], 0, + &aar->assigned_access_rights); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = decode_context_boolean2(&apdu[apdu_len], 1, &aar->enable); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + return apdu_len; +} + +int bacapp_decode_context_assigned_access_rights( + uint8_t * apdu, + uint8_t tag, + BACNET_ASSIGNED_ACCESS_RIGHTS * aar) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag)) { + len++; + section_length = bacapp_decode_assigned_access_rights(&apdu[len], aar); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} diff --git a/bacnet-stack/src/authentication_factor.c b/bacnet-stack/src/authentication_factor.c new file mode 100644 index 00000000..1759f520 --- /dev/null +++ b/bacnet-stack/src/authentication_factor.c @@ -0,0 +1,142 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include "authentication_factor.h" +#include "bacdcode.h" + + +int bacapp_encode_authentication_factor( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR * af) +{ + int len; + int apdu_len = 0; + + len = encode_context_enumerated(&apdu[apdu_len], 0, af->format_type); + if (len < 0) + return -1; + else + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 1, af->format_class); + if (len < 0) + return -1; + else + apdu_len += len; + + len = encode_context_octet_string(&apdu[apdu_len], 2, &af->value); + if (len < 0) + return -1; + else + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR * af) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag); + apdu_len += len; + + len = bacapp_encode_authentication_factor(&apdu[apdu_len], af); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag); + apdu_len += len; + + return apdu_len; + +} + +int bacapp_decode_authentication_factor( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR * af) +{ + int len; + int apdu_len = 0; + + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + len = decode_context_enumerated(&apdu[apdu_len], 0, &af->format_type); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = decode_context_unsigned(&apdu[apdu_len], 1, &af->format_class); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (decode_is_context_tag(&apdu[apdu_len], 2)) { + len = decode_context_octet_string(&apdu[apdu_len], 2, &af->value); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + return apdu_len; +} + +int bacapp_decode_context_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR * af) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag)) { + len++; + section_length = bacapp_decode_authentication_factor(&apdu[len], af); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} diff --git a/bacnet-stack/src/authentication_factor_format.c b/bacnet-stack/src/authentication_factor_format.c new file mode 100644 index 00000000..504a52a7 --- /dev/null +++ b/bacnet-stack/src/authentication_factor_format.c @@ -0,0 +1,151 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include "bacdcode.h" +#include "authentication_factor_format.h" + + +int bacapp_encode_authentication_factor_format( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff) +{ + int len; + int apdu_len = 0; + + len = encode_context_enumerated(&apdu[apdu_len], 0, aff->format_type); + if (len < 0) + return -1; + else + apdu_len += len; + + if (aff->format_type == AUTHENTICATION_FACTOR_TYPE_CUSTOM) { + len = encode_context_unsigned(&apdu[apdu_len], 1, aff->vendor_id); + if (len < 0) + return -1; + else + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 2, aff->vendor_format); + if (len < 0) + return -1; + else + apdu_len += len; + } + return apdu_len; +} + +int bacapp_encode_context_authentication_factor_format( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag); + apdu_len += len; + + len = bacapp_encode_authentication_factor_format(&apdu[apdu_len], aff); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag); + apdu_len += len; + + return apdu_len; + +} + +int bacapp_decode_authentication_factor_format( + uint8_t * apdu, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff) +{ + int len; + int apdu_len = 0; + + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + len = + decode_context_enumerated(&apdu[apdu_len], 0, + &aff->format_type); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = decode_context_unsigned(&apdu[apdu_len], 1, &aff->vendor_id); + if (len < 0) + return -1; + else + apdu_len += len; + if ((aff->format_type != AUTHENTICATION_FACTOR_TYPE_CUSTOM) + && (aff->vendor_id != 0)) + return -1; + } + + if (decode_is_context_tag(&apdu[apdu_len], 2)) { + len = decode_context_unsigned(&apdu[apdu_len], 2, &aff->vendor_format); + if (len < 0) + return -1; + else + apdu_len += len; + if ((aff->format_type != AUTHENTICATION_FACTOR_TYPE_CUSTOM) + && (aff->vendor_format != 0)) + return -1; + } + + return apdu_len; +} + +int bacapp_decode_context_authentication_factor_format( + uint8_t * apdu, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR_FORMAT * aff) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag)) { + len++; + section_length = + bacapp_decode_authentication_factor_format(&apdu[len], aff); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; + +} diff --git a/bacnet-stack/src/bactext.c b/bacnet-stack/src/bactext.c index 9fe81b83..62225f9f 100644 --- a/bacnet-stack/src/bactext.c +++ b/bacnet-stack/src/bactext.c @@ -812,7 +812,7 @@ INDTEXT_DATA bacnet_property_names[] = { , {PROP_EXIT_POINTS, "exit-points"} , - {PROP_EXPIRY_TIME, "expiry-time"} + {PROP_EXPIRATION_TIME, "expiration-time"} , {PROP_EXTENDED_TIME_ENABLE, "extended-time-enable"} , diff --git a/bacnet-stack/src/credential_authentication_factor.c b/bacnet-stack/src/credential_authentication_factor.c new file mode 100644 index 00000000..24ccbd79 --- /dev/null +++ b/bacnet-stack/src/credential_authentication_factor.c @@ -0,0 +1,132 @@ +/************************************************************************** +* +* Copyright (C) 2015 Nikola Jelic +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#include "credential_authentication_factor.h" +#include "bacdcode.h" + + +int bacapp_encode_credential_authentication_factor( + uint8_t * apdu, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf) +{ + int len; + int apdu_len = 0; + + len = encode_context_enumerated(&apdu[apdu_len], 0, caf->disable); + if (len < 0) + return -1; + else + apdu_len += len; + + len = + bacapp_encode_context_authentication_factor(&apdu[apdu_len], 1, + &caf->authentication_factor); + if (len < 0) + return -1; + else + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_context_credential_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag); + apdu_len += len; + + len = bacapp_encode_credential_authentication_factor(&apdu[apdu_len], caf); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag); + apdu_len += len; + + return apdu_len; + +} + +int bacapp_decode_credential_authentication_factor( + uint8_t * apdu, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf) +{ + int len; + int apdu_len = 0; + + if (decode_is_context_tag(&apdu[apdu_len], 0)) { + len = decode_context_enumerated(&apdu[apdu_len], 0, &caf->disable); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + if (decode_is_context_tag(&apdu[apdu_len], 1)) { + len = + bacapp_decode_context_authentication_factor(&apdu[apdu_len], 1, + &caf->authentication_factor); + if (len < 0) + return -1; + else + apdu_len += len; + } else + return -1; + + return apdu_len; +} + +int bacapp_decode_context_credential_authentication_factor( + uint8_t * apdu, + uint8_t tag, + BACNET_CREDENTIAL_AUTHENTICATION_FACTOR * caf) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag)) { + len++; + section_length = + bacapp_decode_credential_authentication_factor(&apdu[len], caf); + + if (section_length == -1) { + len = -1; + } else { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag)) { + len++; + } else { + len = -1; + } + } + } else { + len = -1; + } + return len; +} diff --git a/bacnet-stack/test.mak b/bacnet-stack/test.mak index c5150c93..55aa36d5 100644 --- a/bacnet-stack/test.mak +++ b/bacnet-stack/test.mak @@ -206,7 +206,45 @@ wp: logfile test/wp.mak ( ./test/wp >> ${LOGFILE} ) $(MAKE) -s -C test -f wp.mak clean -objects: ai ao av bi bo bv csv lc lo lso lsp mso msv ms-input osv piv command +objects: ai ao av bi bo bv csv lc lo lso lsp \ + mso msv ms-input osv piv command \ + access_credential access_door access_point access_rights \ + access_user access_zone credential_data_input + +access_credential: logfile demo/object/access_credential.mak + $(MAKE) -s -C demo/object -f access_credential.mak clean all + ( ./demo/object/access_credential >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_credential.mak clean + +access_door: logfile demo/object/access_door.mak + $(MAKE) -s -C demo/object -f access_door.mak clean all + ( ./demo/object/access_door >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_door.mak clean + +access_point: logfile demo/object/access_point.mak + $(MAKE) -s -C demo/object -f access_point.mak clean all + ( ./demo/object/access_point >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_point.mak clean + +access_rights: logfile demo/object/access_rights.mak + $(MAKE) -s -C demo/object -f access_rights.mak clean all + ( ./demo/object/access_rights >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_rights.mak clean + +access_user: logfile demo/object/access_user.mak + $(MAKE) -s -C demo/object -f access_user.mak clean all + ( ./demo/object/access_user >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_user.mak clean + +access_zone: logfile demo/object/access_zone.mak + $(MAKE) -s -C demo/object -f access_zone.mak clean all + ( ./demo/object/access_zone >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f access_zone.mak clean + +credential_data_input: logfile demo/object/credential_data_input.mak + $(MAKE) -s -C demo/object -f credential_data_input.mak clean all + ( ./demo/object/credential_data_input >> ${LOGFILE} ) + $(MAKE) -s -C demo/object -f credential_data_input.mak clean ai: logfile demo/object/ai.mak $(MAKE) -s -C demo/object -f ai.mak clean all