Issue 2 move folders and use deep path include file names to prevent collisions (#4)

* moving folders and files and adjust server demo build

* Fix Makefile for apps/server on Linux

* fix unit test source file folders

* fix datetime convert UTC functions. Add Code::Blocks project for datetime testing

* added some ignore extensions

* disable parallel make option

* fix build for abort, dcc, and epics apps

* fix build for dcc, epics, error, and getevent apps.

* Fixed building of all apps

* fix the ipv4 to ipv6 router app build

* Change indent style from Google to Webkit

* make pretty to re-format style

* removed common Makefile since we already had one and two was too many

* remove scripts from root folder that are no longer maintained or used

* remove mercurial EOL and ignore files for git repo

* remove .vscodeconfig files from repo

* tweak clang-format style

* clang-format src and apps with tweaked style

* added clang-tidy to fix readability if braces in src

* result of make tidy for src and apps

* fix clang-tidy mangling

* Added code::blocks project for BACnet server simulation

* added code::blocks linux project for WhoIs app

* update text files for EOL

* fix EOL in some files

* fixed make win32 apps for older gcc

* Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler.

* created codeblocks project for apps/epics for Windows

* fixing ports/xplained to work with new data structure.

* fix ports/xplained example for Atmel Studio compile

* fix ports/stm32f10x example for gcc Makefile compile

* fix ports/stm32f10x example for IAR EWARM compile

* fix ports/xplained timer callback

* fix ports/bdk_atxx_mspt build with subdirs

* fix ports/bdk_atxx_mspt build with subdirs

* updated git ignore for IAR build artifacts

* updated gitignore for non-tracked files and folders

* fixed bdk-atxx4-mstp port for Rowley Crossworks project file

* fixed bdk-atxx4-mstp port for GCC AVR Makefile

* fixed atmega168 port for IAR AVR and GCC AVR Makefile

* fixed at91sam7s port for IAR ARM and GCC ARM Makefile

* removed unmaintainable DOS, RTOS32, and atmega8 ports.  Updated rx62n (untested).

* changed arm7 to uip port
This commit is contained in:
Steve Karg
2019-12-13 15:19:10 -06:00
committed by GitHub
parent 8a38dbe2cf
commit d50c190957
912 changed files with 36206 additions and 52502 deletions
+146
View File
@@ -0,0 +1,146 @@
# Unit tests for the BACnet Stack project
LOGFILE = test.log
all: ai ao av bi bo bv csv lc lo lsp \
mso msv ms-input netport osv piv command \
access_credential access_door access_point access_rights \
access_user access_zone credential_data_input
clean: logfile
rm ${LOGFILE}
logfile:
touch ${LOGFILE}
report:
cat ${LOGFILE}
access_credential: logfile access_credential.mak
$(MAKE) -s -f access_credential.mak clean all
( ./access_credential >> ${LOGFILE} )
$(MAKE) -s -f access_credential.mak clean
access_door: logfile access_door.mak
$(MAKE) -s -f access_door.mak clean all
( ./access_door >> ${LOGFILE} )
$(MAKE) -s -f access_door.mak clean
access_point: logfile access_point.mak
$(MAKE) -s -f access_point.mak clean all
( ./access_point >> ${LOGFILE} )
$(MAKE) -s -f access_point.mak clean
access_rights: logfile access_rights.mak
$(MAKE) -s -f access_rights.mak clean all
( ./access_rights >> ${LOGFILE} )
$(MAKE) -s -f access_rights.mak clean
access_user: logfile access_user.mak
$(MAKE) -s -f access_user.mak clean all
( ./access_user >> ${LOGFILE} )
$(MAKE) -s -f access_user.mak clean
access_zone: logfile access_zone.mak
$(MAKE) -s -f access_zone.mak clean all
( ./access_zone >> ${LOGFILE} )
$(MAKE) -s -f access_zone.mak clean
credential_data_input: logfile credential_data_input.mak
$(MAKE) -s -f credential_data_input.mak clean all
( ./credential_data_input >> ${LOGFILE} )
$(MAKE) -s -f credential_data_input.mak clean
ai: logfile ai.mak
$(MAKE) -s -f ai.mak clean all
( ./analog_input >> ${LOGFILE} )
$(MAKE) -s -f ai.mak clean
ao: logfile ao.mak
$(MAKE) -s -f ao.mak clean all
( ./analog_output >> ${LOGFILE} )
$(MAKE) -s -f ao.mak clean
av: logfile av.mak
$(MAKE) -s -f av.mak clean all
( ./analog_value >> ${LOGFILE} )
$(MAKE) -s -f av.mak clean
bi: logfile bi.mak
$(MAKE) -s -f bi.mak clean all
$(MAKE) -s -f bi.mak clean
bo: logfile bo.mak
$(MAKE) -s -f bo.mak clean all
( ./binary_output >> ${LOGFILE} )
$(MAKE) -s -f bo.mak clean
bv: logfile bv.mak
$(MAKE) -s -f bv.mak clean all
( ./binary_value >> ${LOGFILE} )
$(MAKE) -s -f bv.mak clean
command: logfile command.mak
$(MAKE) -s -f command.mak clean all
( ./command >> ${LOGFILE} )
$(MAKE) -s -f command.mak clean
csv: logfile csv.mak
$(MAKE) -s -f csv.mak clean all
( ./characterstring_value >> ${LOGFILE} )
$(MAKE) -s -f csv.mak clean
device: logfile device.mak
$(MAKE) -s -f device.mak clean all
( ./device >> ${LOGFILE} )
$(MAKE) -s -f device.mak clean
lc: logfile lc.mak
$(MAKE) -s -f lc.mak clean all
( ./load_control >> ${LOGFILE} )
$(MAKE) -s -f lc.mak clean
lo: logfile lo.mak
$(MAKE) -s -f lo.mak clean all
( ./lighting_output >> ${LOGFILE} )
$(MAKE) -s -f lo.mak clean
lsp: logfile lsp.mak
$(MAKE) -s -f lsp.mak clean all
( ./life_safety_point >> ${LOGFILE} )
$(MAKE) -s -f lsp.mak clean
ms-input: logfile ms-input.mak
$(MAKE) -s -f ms-input.mak clean all
( ./multistate_input >> ${LOGFILE} )
$(MAKE) -s -f ms-input.mak clean
mso: logfile mso.mak
$(MAKE) -s -f mso.mak clean all
( ./multistate_output >> ${LOGFILE} )
$(MAKE) -s -f mso.mak clean
msv: logfile msv.mak
$(MAKE) -s -f msv.mak clean all
( ./multistate_value >> ${LOGFILE} )
$(MAKE) -s -f msv.mak clean
osv: logfile osv.mak
$(MAKE) -s -f osv.mak clean all
( ./octetstring_value >> ${LOGFILE} )
$(MAKE) -s -f osv.mak clean
netport: logfile netport.mak
$(MAKE) -s -f netport.mak clean all
( ./network_port >> ${LOGFILE} )
$(MAKE) -s -f netport.mak clean
piv: logfile piv.mak
$(MAKE) -s -f piv.mak clean all
( ./positiveinteger_value >> ${LOGFILE} )
$(MAKE) -s -f piv.mak clean
schedule: logfile schedule.mak
$(MAKE) -s -f schedule.mak clean all
( ./schedule >> ${LOGFILE} )
$(MAKE) -s -f schedule.mak clean
+442
View File
@@ -0,0 +1,442 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/basic/object/access_credential.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+124
View File
@@ -0,0 +1,124 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/datetime.h"
#include "bacnet/timestamp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/assigned_access_rights.h"
#include "bacnet/credential_authentication_factor.h"
#include "bacnet/rp.h"
#include "bacnet/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
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/assigned_access_rights.c \
$(SRC_DIR)/bacnet/authentication_factor.c \
$(SRC_DIR)/bacnet/credential_authentication_factor.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/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
+632
View File
@@ -0,0 +1,632 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_door.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+143
View File
@@ -0,0 +1,143 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/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
+42
View File
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/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
+431
View File
@@ -0,0 +1,431 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_point.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/timestamp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/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
+43
View File
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/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
+404
View File
@@ -0,0 +1,404 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_rights.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+110
View File
@@ -0,0 +1,110 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/access_rule.h"
#include "bacnet/rp.h"
#include "bacnet/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
+43
View File
@@ -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)/bacnet/access_rule.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/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
+346
View File
@@ -0,0 +1,346 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_user.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+103
View File
@@ -0,0 +1,103 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/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
+42
View File
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/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
+411
View File
@@ -0,0 +1,411 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_zone.h"
#include "bacnet/basic/services.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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
+117
View File
@@ -0,0 +1,117 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/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
+42
View File
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/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
File diff suppressed because it is too large Load Diff
+174
View File
@@ -0,0 +1,174 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* 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 AI_H
#define AI_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h"
#include "bacnet/getevent.h"
#include "bacnet/alarm_ack.h"
#include "bacnet/get_alarm_sum.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct analog_input_descr {
unsigned Event_State:3;
float Present_Value;
BACNET_RELIABILITY Reliability;
bool Out_Of_Service;
uint8_t Units;
float Prior_Value;
float COV_Increment;
bool Changed;
#if defined(INTRINSIC_REPORTING)
uint32_t Time_Delay;
uint32_t Notification_Class;
float High_Limit;
float Low_Limit;
float Deadband;
unsigned Limit_Enable:2;
unsigned Event_Enable:3;
unsigned Notify_Type:1;
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
/* time to generate event notification */
uint32_t Remaining_Time_Delay;
/* AckNotification informations */
ACK_NOTIFICATION Ack_notify_data;
#endif
} ANALOG_INPUT_DESCR;
void Analog_Input_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Input_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Input_Count(
void);
uint32_t Analog_Input_Index_To_Instance(
unsigned index);
unsigned Analog_Input_Instance_To_Index(
uint32_t instance);
bool Analog_Input_Object_Instance_Add(
uint32_t instance);
bool Analog_Input_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Input_Name_Set(
uint32_t object_instance,
char *new_name);
char *Analog_Input_Description(
uint32_t instance);
bool Analog_Input_Description_Set(
uint32_t instance,
char *new_name);
bool Analog_Input_Units_Set(
uint32_t instance,
uint16_t units);
uint16_t Analog_Input_Units(
uint32_t instance);
int Analog_Input_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Input_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
float Analog_Input_Present_Value(
uint32_t object_instance);
void Analog_Input_Present_Value_Set(
uint32_t object_instance,
float value);
bool Analog_Input_Out_Of_Service(
uint32_t object_instance);
void Analog_Input_Out_Of_Service_Set(
uint32_t object_instance,
bool oos_flag);
bool Analog_Input_Change_Of_Value(
uint32_t instance);
void Analog_Input_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Input_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Input_COV_Increment(
uint32_t instance);
void Analog_Input_COV_Increment_Set(
uint32_t instance,
float value);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Analog_Input_Intrinsic_Reporting(
uint32_t object_instance);
#if defined(INTRINSIC_REPORTING)
int Analog_Input_Event_Information(
unsigned index,
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
int Analog_Input_Alarm_Ack(
BACNET_ALARM_ACK_DATA * alarmack_data,
BACNET_ERROR_CODE * error_code);
int Analog_Input_Alarm_Summary(
unsigned index,
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
#endif
bool Analog_Input_Create(
uint32_t object_instance);
bool Analog_Input_Delete(
uint32_t object_instance);
void Analog_Input_Cleanup(
void);
void Analog_Input_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalogInput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -0,0 +1,43 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
HANDLER_DIR = ../handler
INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR)
DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_ANALOG_INPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = ai.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = analog_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
+558
View File
@@ -0,0 +1,558 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Analog Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/basic/object/ao.h"
#include "bacnet/basic/services.h"
#ifndef MAX_ANALOG_OUTPUTS
#define MAX_ANALOG_OUTPUTS 4
#endif
/* we choose to have a NULL level in our system represented by */
/* a particular value. When the priorities are not in use, they */
/* will be relinquished (i.e. set to the NULL level). */
#define AO_LEVEL_NULL 255
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define AO_RELINQUISH_DEFAULT 0
/* Here is our Priority Array. They are supposed to be Real, but */
/* we don't have that kind of memory, so we will use a single byte */
/* and load a Real for returning the value when asked. */
static uint8_t Analog_Output_Level[MAX_ANALOG_OUTPUTS][BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_ANALOG_OUTPUTS];
/* we need to have our arrays initialized before answering any calls */
static bool Analog_Output_Initialized = false;
/* 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_OUT_OF_SERVICE, PROP_UNITS, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Analog_Output_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 Analog_Output_Init(void)
{
unsigned i, j;
if (!Analog_Output_Initialized) {
Analog_Output_Initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Analog_Output_Level[i][j] = AO_LEVEL_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Analog_Output_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ANALOG_OUTPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Analog_Output_Count(void)
{
return MAX_ANALOG_OUTPUTS;
}
/* 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 Analog_Output_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 Analog_Output_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ANALOG_OUTPUTS;
if (object_instance < MAX_ANALOG_OUTPUTS) {
index = object_instance;
}
return index;
}
float Analog_Output_Present_Value(uint32_t object_instance)
{
float value = AO_RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
value = Analog_Output_Level[index][i];
break;
}
}
}
return value;
}
unsigned Analog_Output_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 = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
priority = i + 1;
break;
}
}
}
return priority;
}
bool Analog_Output_Present_Value_Set(
uint32_t object_instance, float value, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) && (value >= 0.0) &&
(value <= 100.0)) {
Analog_Output_Level[index][priority - 1] = (uint8_t)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 Analog_Output_Present_Value_Relinquish(
uint32_t object_instance, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */)) {
Analog_Output_Level[index][priority - 1] = AO_LEVEL_NULL;
/* 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;
}
/* note: the object name must be unique within this device */
bool Analog_Output_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_ANALOG_OUTPUTS) {
sprintf(
text_string, "ANALOG OUTPUT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Analog_Output_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Analog_Output_Instance_To_Index(instance);
if (index < MAX_ANALOG_OUTPUTS) {
oos_flag = Out_Of_Service[index];
}
return oos_flag;
}
void Analog_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Analog_Output_Instance_To_Index(instance);
if (index < MAX_ANALOG_OUTPUTS) {
Out_Of_Service[index] = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Analog_Output_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;
float real_value = (float)1.414;
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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ANALOG_OUTPUT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Analog_Output_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_ANALOG_OUTPUT);
break;
case PROP_PRESENT_VALUE:
real_value = Analog_Output_Present_Value(rpdata->object_instance);
apdu_len = encode_application_real(&apdu[0], real_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 = Analog_Output_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], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Analog_Output_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_UNITS:
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT);
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) {
object_index =
Analog_Output_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
real_value = Analog_Output_Level[object_index][i];
len = encode_application_real(
&apdu[apdu_len], real_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Analog_Output_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Analog_Output_Level[object_index][rpdata->array_index -
1] == AO_LEVEL_NULL) {
apdu_len = encode_application_null(&apdu[0]);
} else {
real_value =
Analog_Output_Level[object_index]
[rpdata->array_index - 1];
apdu_len =
encode_application_real(&apdu[0], real_value);
}
} 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:
real_value = AO_RELINQUISH_DEFAULT;
apdu_len = encode_application_real(&apdu[0], real_value);
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 Analog_Output_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_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;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
status =
Analog_Output_Present_Value_Set(wp_data->object_instance,
value.type.Real, 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 = Analog_Output_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) {
Analog_Output_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_UNITS:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
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 <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
bool bResult;
/*
* start out assuming success and only set up error
* response if validation fails.
*/
bResult = true;
if (pValue->tag != ucExpectedTag) {
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
}
return (bResult);
}
void testAnalogOutput(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;
Analog_Output_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ANALOG_OUTPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Analog_Output_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_ANALOG_OUTPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Analog Output", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAnalogOutput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ANALOG_INPUT */
#endif /* TEST */
+139
View File
@@ -0,0 +1,139 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 AO_H
#define AO_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Analog_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Output_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Output_Count(
void);
uint32_t Analog_Output_Index_To_Instance(
unsigned index);
unsigned Analog_Output_Instance_To_Index(
uint32_t instance);
bool Analog_Output_Object_Instance_Add(
uint32_t instance);
float Analog_Output_Present_Value(
uint32_t object_instance);
unsigned Analog_Output_Present_Value_Priority(
uint32_t object_instance);
bool Analog_Output_Present_Value_Set(
uint32_t object_instance,
float value,
unsigned priority);
bool Analog_Output_Present_Value_Relinquish(
uint32_t object_instance,
unsigned priority);
float Analog_Output_Relinquish_Default(
uint32_t object_instance);
bool Analog_Output_Relinquish_Default_Set(
uint32_t object_instance,
float value);
bool Analog_Output_Change_Of_Value(
uint32_t instance);
void Analog_Output_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Output_COV_Increment(
uint32_t instance);
void Analog_Output_COV_Increment_Set(
uint32_t instance,
float value);
bool Analog_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Output_Name_Set(
uint32_t object_instance,
char *new_name);
char *Analog_Output_Description(
uint32_t instance);
bool Analog_Output_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Analog_Output_Reliability(
uint32_t object_instance);
bool Analog_Output_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
bool Analog_Output_Units_Set(
uint32_t instance,
uint16_t units);
uint16_t Analog_Output_Units(
uint32_t instance);
bool Analog_Output_Out_Of_Service(
uint32_t instance);
void Analog_Output_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
int Analog_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Analog_Output_Create(
uint32_t object_instance);
bool Analog_Output_Delete(
uint32_t object_instance);
void Analog_Output_Cleanup(
void);
void Analog_Output_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalogOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_ANALOG_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = ao.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = analog_output
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
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* 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 AV_H
#define AV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h"
#include "bacnet/alarm_ack.h"
#include "bacnet/getevent.h"
#include "bacnet/get_alarm_sum.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct analog_value_descr {
unsigned Event_State:3;
bool Out_Of_Service;
uint16_t Units;
float Present_Value;
float Prior_Value;
float COV_Increment;
bool Changed;
#if defined(INTRINSIC_REPORTING)
uint32_t Time_Delay;
uint32_t Notification_Class;
float High_Limit;
float Low_Limit;
float Deadband;
unsigned Limit_Enable:2;
unsigned Event_Enable:3;
unsigned Notify_Type:1;
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
/* time to generate event notification */
uint32_t Remaining_Time_Delay;
/* AckNotification informations */
ACK_NOTIFICATION Ack_notify_data;
#endif
} ANALOG_VALUE_DESCR;
void Analog_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Value_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Value_Count(
void);
uint32_t Analog_Value_Index_To_Instance(
unsigned index);
unsigned Analog_Value_Instance_To_Index(
uint32_t object_instance);
bool Analog_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Value_Name_Set(
uint32_t object_instance,
char *new_name);
int Analog_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Analog_Value_Present_Value_Set(
uint32_t object_instance,
float value,
uint8_t priority);
float Analog_Value_Present_Value(
uint32_t object_instance);
bool Analog_Value_Change_Of_Value(
uint32_t instance);
void Analog_Value_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Value_COV_Increment(
uint32_t instance);
void Analog_Value_COV_Increment_Set(
uint32_t instance,
float value);
char *Analog_Value_Description(
uint32_t instance);
bool Analog_Value_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Analog_Value_Reliability(
uint32_t object_instance);
bool Analog_Value_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
uint16_t Analog_Value_Units(
uint32_t instance);
bool Analog_Value_Units_Set(
uint32_t instance,
uint16_t unit);
bool Analog_Value_Out_Of_Service(
uint32_t instance);
void Analog_Value_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Analog_Value_Intrinsic_Reporting(
uint32_t object_instance);
#if defined(INTRINSIC_REPORTING)
int Analog_Value_Event_Information(
unsigned index,
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
int Analog_Value_Alarm_Ack(
BACNET_ALARM_ACK_DATA * alarmack_data,
BACNET_ERROR_CODE * error_code);
int Analog_Value_Alarm_Summary(
unsigned index,
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
#endif
bool Analog_Value_Create(
uint32_t object_instance);
bool Analog_Value_Delete(
uint32_t object_instance);
void Analog_Value_Cleanup(
void);
void Analog_Value_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalog_Value(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_ANALOG_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = av.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = analog_value
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
+603
View File
@@ -0,0 +1,603 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bacnet/config.h"
#include "bacnet/basic/binding/address.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacapp.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/bacdcode.h"
#include "bacnet/npdu.h"
#include "bacnet/apdu.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/arf.h"
#include "bacnet/awf.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/bacfile.h"
typedef struct {
uint32_t instance;
char *filename;
} BACNET_FILE_LISTING;
#ifndef FILE_RECORD_SIZE
#define FILE_RECORD_SIZE MAX_OCTET_STRING_BYTES
#endif
static BACNET_FILE_LISTING BACnet_File_Listing[] = {
{ 0, "temp_0.txt" }, { 1, "temp_1.txt" }, { 2, "temp_2.txt" },
{ 0, NULL } /* last file indication */
};
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int bacfile_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_FILE_TYPE, PROP_FILE_SIZE,
PROP_MODIFICATION_DATE, PROP_ARCHIVE, PROP_READ_ONLY,
PROP_FILE_ACCESS_METHOD, -1 };
static const int bacfile_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int bacfile_Properties_Proprietary[] = { -1 };
void BACfile_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = bacfile_Properties_Required;
}
if (pOptional) {
*pOptional = bacfile_Properties_Optional;
}
if (pProprietary) {
*pProprietary = bacfile_Properties_Proprietary;
}
return;
}
static char *bacfile_name(uint32_t instance)
{
uint32_t index = 0;
char *filename = NULL;
/* linear search for file instance match */
while (BACnet_File_Listing[index].filename) {
if (BACnet_File_Listing[index].instance == instance) {
filename = BACnet_File_Listing[index].filename;
break;
}
index++;
}
return filename;
}
bool bacfile_object_name(
uint32_t instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
char *filename = NULL;
filename = bacfile_name(instance);
if (filename) {
status = characterstring_init_ansi(object_name, filename);
}
return status;
}
bool bacfile_valid_instance(uint32_t object_instance)
{
return bacfile_name(object_instance) ? true : false;
}
uint32_t bacfile_count(void)
{
uint32_t index = 0;
/* linear search for file instance match */
while (BACnet_File_Listing[index].filename) {
index++;
}
return index;
}
uint32_t bacfile_index_to_instance(unsigned find_index)
{
uint32_t instance = BACNET_MAX_INSTANCE + 1;
uint32_t index = 0;
/* bounds checking... */
while (BACnet_File_Listing[index].filename) {
if (index == find_index) {
instance = BACnet_File_Listing[index].instance;
break;
}
index++;
}
return instance;
}
static long fsize(FILE *pFile)
{
long size = 0;
long origin = 0;
if (pFile) {
origin = ftell(pFile);
fseek(pFile, 0L, SEEK_END);
size = ftell(pFile);
fseek(pFile, origin, SEEK_SET);
}
return (size);
}
unsigned bacfile_file_size(uint32_t object_instance)
{
char *pFilename = NULL;
FILE *pFile = NULL;
unsigned file_size = 0;
pFilename = bacfile_name(object_instance);
if (pFilename) {
pFile = fopen(pFilename, "rb");
if (pFile) {
file_size = fsize(pFile);
fclose(pFile);
}
}
return file_size;
}
/* return the number of bytes used, or -1 on error */
int bacfile_read_property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
char text_string[32] = { "" };
BACNET_CHARACTER_STRING char_string;
BACNET_DATE bdate;
BACNET_TIME btime;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_FILE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
sprintf(text_string, "FILE %lu",
(unsigned long)rpdata->object_instance);
characterstring_init_ansi(&char_string, text_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_FILE);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string, bacfile_name(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_FILE_TYPE:
characterstring_init_ansi(&char_string, "TEXT");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_FILE_SIZE:
apdu_len = encode_application_unsigned(
&apdu[0], bacfile_file_size(rpdata->object_instance));
break;
case PROP_MODIFICATION_DATE:
/* FIXME: get the actual value instead of April Fool's Day */
bdate.year = 2006; /* AD */
bdate.month = 4; /* 1=Jan */
bdate.day = 1; /* 1..31 */
bdate.wday = 6; /* 1=Monday */
apdu_len = encode_application_date(&apdu[0], &bdate);
/* FIXME: get the actual value */
btime.hour = 7;
btime.min = 0;
btime.sec = 3;
btime.hundredths = 1;
apdu_len += encode_application_time(&apdu[apdu_len], &btime);
break;
case PROP_ARCHIVE:
/* 12.13.8 Archive
This property, of type BOOLEAN, indicates whether the File
object has been saved for historical or backup purposes. This
property shall be logical TRUE only if no changes have been
made to the file data by internal processes or through File
Access Services since the last time the object was archived.
*/
/* FIXME: get the actual value: note it may be inverse... */
apdu_len = encode_application_boolean(&apdu[0], true);
break;
case PROP_READ_ONLY:
/* FIXME: get the actual value */
apdu_len = encode_application_boolean(&apdu[0], true);
break;
case PROP_FILE_ACCESS_METHOD:
apdu_len = encode_application_enumerated(
&apdu[0], FILE_RECORD_AND_STREAM_ACCESS);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
return apdu_len;
}
/* returns true if successful */
bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
if (!bacfile_valid_instance(wp_data->object_instance)) {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
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;
}
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
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;
}
/* FIXME: len < application_data_len: more data? */
switch (wp_data->object_property) {
case PROP_ARCHIVE:
/* 12.13.8 Archive
This property, of type BOOLEAN, indicates whether the File
object has been saved for historical or backup purposes. This
property shall be logical TRUE only if no changes have been
made to the file data by internal processes or through File
Access Services since the last time the object was archived. */
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Boolean) {
/* FIXME: do something to wp_data->object_instance */
} else {
/* FIXME: do something to wp_data->object_instance */
}
}
break;
case PROP_FILE_SIZE:
/* If the file size can be changed by writing to the file,
and File_Access_Method is STREAM_ACCESS, then this property
shall be writable. */
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
/* FIXME: do something with value.type.Unsigned
to wp_data->object_instance */
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_DESCRIPTION:
case PROP_FILE_TYPE:
case PROP_MODIFICATION_DATE:
case PROP_READ_ONLY:
case PROP_FILE_ACCESS_METHOD:
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;
}
uint32_t bacfile_instance(char *filename)
{
uint32_t index = 0;
uint32_t instance = BACNET_MAX_INSTANCE + 1;
/* linear search for filename match */
while (BACnet_File_Listing[index].filename) {
if (strcmp(BACnet_File_Listing[index].filename, filename) == 0) {
instance = BACnet_File_Listing[index].instance;
break;
}
index++;
}
return instance;
}
#if MAX_TSM_TRANSACTIONS
/* this is one way to match up the invoke ID with */
/* the file ID from the AtomicReadFile request. */
/* Another way would be to store the */
/* invokeID and file instance in a list or table */
/* when the request was sent */
uint32_t bacfile_instance_from_tsm(uint8_t invokeID)
{
BACNET_NPDU_DATA npdu_data = { 0 }; /* dummy for getting npdu length */
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
uint8_t service_choice = 0;
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
BACNET_ADDRESS dest; /* where the original packet was destined */
uint8_t apdu[MAX_PDU] = { 0 }; /* original APDU packet */
uint16_t apdu_len = 0; /* original APDU packet length */
int len = 0; /* apdu header length */
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
uint32_t object_instance = BACNET_MAX_INSTANCE + 1; /* return value */
bool found = false;
found = tsm_get_transaction_pdu(
invokeID, &dest, &npdu_data, &apdu[0], &apdu_len);
if (found) {
if (!npdu_data.network_layer_message &&
npdu_data.data_expecting_reply &&
(apdu[0] == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len,
&service_data, &service_choice, &service_request,
&service_request_len);
if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
len = arf_decode_service_request(
service_request, service_request_len, &data);
if (len > 0) {
if (data.object_type == OBJECT_FILE) {
object_instance = data.object_instance;
}
}
}
}
}
return object_instance;
}
#endif
bool bacfile_read_stream_data(BACNET_ATOMIC_READ_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
size_t len = 0;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
len = fread(octetstring_value(&data->fileData[0]), 1,
data->type.stream.requestedOctetCount, pFile);
if (len < data->type.stream.requestedOctetCount) {
data->endOfFile = true;
} else {
data->endOfFile = false;
}
octetstring_truncate(&data->fileData[0], len);
fclose(pFile);
} else {
octetstring_truncate(&data->fileData[0], 0);
data->endOfFile = true;
}
} else {
octetstring_truncate(&data->fileData[0], 0);
data->endOfFile = true;
}
return found;
}
bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
if (data->type.stream.fileStartPosition == 0) {
/* open the file as a clean slate when starting at 0 */
pFile = fopen(pFilename, "wb");
} else if (data->type.stream.fileStartPosition == -1) {
/* If 'File Start Position' parameter has the special
value -1, then the write operation shall be treated
as an append to the current end of file. */
pFile = fopen(pFilename, "ab+");
} else {
/* open for update */
pFile = fopen(pFilename, "rb+");
}
if (pFile) {
if (data->type.stream.fileStartPosition != -1) {
(void)fseek(
pFile, data->type.stream.fileStartPosition, SEEK_SET);
}
if (fwrite(octetstring_value(&data->fileData[0]),
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
/* do something if it fails? */
}
fclose(pFile);
}
}
return found;
}
bool bacfile_write_record_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
uint32_t i = 0;
char dummy_data[FILE_RECORD_SIZE];
char *pData = NULL;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
if (data->type.record.fileStartRecord == 0) {
/* open the file as a clean slate when starting at 0 */
pFile = fopen(pFilename, "wb");
} else if (data->type.record.fileStartRecord == -1) {
/* If 'File Start Record' parameter has the special
value -1, then the write operation shall be treated
as an append to the current end of file. */
pFile = fopen(pFilename, "ab+");
} else {
/* open for update */
pFile = fopen(pFilename, "rb+");
}
if (pFile) {
if ((data->type.record.fileStartRecord != -1) &&
(data->type.record.fileStartRecord > 0)) {
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
i++) {
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
if ((pData == NULL) || feof(pFile)) {
break;
}
}
}
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
if (fwrite(octetstring_value(&data->fileData[i]),
octetstring_length(&data->fileData[i]), 1,
pFile) != 1) {
/* do something if it fails? */
}
}
fclose(pFile);
}
}
return found;
}
bool bacfile_read_ack_stream_data(
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
{
bool found = false;
FILE *pFile = NULL;
char *pFilename = NULL;
pFilename = bacfile_name(instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
if (fwrite(octetstring_value(&data->fileData[0]),
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
(unsigned long)instance);
#endif
}
fclose(pFile);
}
}
return found;
}
bool bacfile_read_ack_record_data(
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
{
bool found = false;
FILE *pFile = NULL;
char *pFilename = NULL;
uint32_t i = 0;
char dummy_data[MAX_OCTET_STRING_BYTES] = { 0 };
char *pData = NULL;
pFilename = bacfile_name(instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
if (data->type.record.fileStartRecord > 0) {
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
i++) {
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
if ((pData == NULL) || feof(pFile)) {
break;
}
}
}
for (i = 0; i < data->type.record.RecordCount; i++) {
if (fwrite(octetstring_value(&data->fileData[i]),
octetstring_length(&data->fileData[i]), 1,
pFile) != 1) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
(unsigned long)instance);
#endif
}
}
fclose(pFile);
}
}
return found;
}
void bacfile_init(void)
{
}
+108
View File
@@ -0,0 +1,108 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#ifndef BACFILE_H
#define BACFILE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/apdu.h"
#include "bacnet/arf.h"
#include "bacnet/awf.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void BACfile_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool bacfile_object_name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool bacfile_valid_instance(
uint32_t object_instance);
uint32_t bacfile_count(
void);
uint32_t bacfile_index_to_instance(
unsigned find_index);
unsigned bacfile_instance_to_index(
uint32_t instance);
uint32_t bacfile_instance(
char *filename);
/* this is one way to match up the invoke ID with */
/* the file ID from the AtomicReadFile request. */
/* Another way would be to store the */
/* invokeID and file instance in a list or table */
/* when the request was sent */
uint32_t bacfile_instance_from_tsm(
uint8_t invokeID);
/* handler ACK helper */
bool bacfile_read_stream_data(
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_read_ack_stream_data(
uint32_t instance,
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_write_stream_data(
BACNET_ATOMIC_WRITE_FILE_DATA * data);
bool bacfile_read_record_data(
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_read_ack_record_data(
uint32_t instance,
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_write_record_data(
BACNET_ATOMIC_WRITE_FILE_DATA * data);
void bacfile_init(
void);
uint32_t bacfile_file_size(
uint32_t instance);
/* handling for read property service */
int bacfile_read_property(
BACNET_READ_PROPERTY_DATA * rpdata);
/* handling for write property service */
bool bacfile_write_property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+552
View File
@@ -0,0 +1,552 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Binary Input Objects customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/cov.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/bi.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_INPUTS
#define MAX_BINARY_INPUTS 5
#endif
/* stores the current value */
static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS];
/* out of service decouples physical input from Present_Value */
static bool Out_Of_Service[MAX_BINARY_INPUTS];
/* Change of Value flag */
static bool Change_Of_Value[MAX_BINARY_INPUTS];
/* Polarity of Input */
static BACNET_POLARITY Polarity[MAX_BINARY_INPUTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Input_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, -1 };
static const int Binary_Input_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Binary_Input_Properties_Proprietary[] = { -1 };
void Binary_Input_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Input_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Input_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Input_Properties_Proprietary;
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Input_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_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 Binary_Input_Count(void)
{
return MAX_BINARY_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 Binary_Input_Index_To_Instance(unsigned index)
{
return index;
}
void Binary_Input_Init(void)
{
static bool initialized = false;
unsigned i;
if (!initialized) {
initialized = true;
/* initialize all the values */
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
Present_Value[i] = BINARY_INACTIVE;
}
}
return;
}
/* 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 Binary_Input_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_INPUTS;
if (object_instance < MAX_BINARY_INPUTS) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = BINARY_INACTIVE;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
value = Present_Value[index];
if (Polarity[index] != POLARITY_NORMAL) {
if (value == BINARY_INACTIVE) {
value = BINARY_ACTIVE;
} else {
value = BINARY_INACTIVE;
}
}
}
return value;
}
bool Binary_Input_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
value = Out_Of_Service[index];
}
return value;
}
bool Binary_Input_Change_Of_Value(uint32_t object_instance)
{
bool status = false;
unsigned index;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
status = Change_Of_Value[index];
}
return status;
}
void Binary_Input_Change_Of_Value_Clear(uint32_t object_instance)
{
unsigned index;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
Change_Of_Value[index] = false;
}
return;
}
/**
* For a given object instance-number, loads the value_list with the COV data.
*
* @param object_instance - object-instance number of the object
* @param value_list - list of COV data
*
* @return true if the value list is encoded
*/
bool Binary_Input_Encode_Value_List(
uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list)
{
bool status = false;
if (value_list) {
value_list->propertyIdentifier = PROP_PRESENT_VALUE;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
value_list->value.next = NULL;
value_list->value.type.Enumerated =
Binary_Input_Present_Value(object_instance);
value_list->priority = BACNET_NO_PRIORITY;
value_list = value_list->next;
}
if (value_list) {
value_list->propertyIdentifier = PROP_STATUS_FLAGS;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING;
value_list->value.next = NULL;
bitstring_init(&value_list->value.type.Bit_String);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false);
if (Binary_Input_Out_Of_Service(object_instance)) {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, true);
} else {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, false);
}
value_list->priority = BACNET_NO_PRIORITY;
value_list->next = NULL;
status = true;
}
return status;
}
bool Binary_Input_Present_Value_Set(
uint32_t object_instance, BACNET_BINARY_PV value)
{
unsigned index = 0;
bool status = false;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
if (Polarity[index] != POLARITY_NORMAL) {
if (value == BINARY_INACTIVE) {
value = BINARY_ACTIVE;
} else {
value = BINARY_INACTIVE;
}
}
if (Present_Value[index] != value) {
Change_Of_Value[index] = true;
}
Present_Value[index] = value;
status = true;
}
return status;
}
void Binary_Input_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
if (Out_Of_Service[index] != value) {
Change_Of_Value[index] = true;
}
Out_Of_Service[index] = value;
}
return;
}
bool Binary_Input_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
sprintf(
text_string, "BINARY INPUT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
BACNET_POLARITY Binary_Input_Polarity(uint32_t object_instance)
{
BACNET_POLARITY polarity = POLARITY_NORMAL;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
polarity = Polarity[index];
}
return polarity;
}
bool Binary_Input_Polarity_Set(
uint32_t object_instance, BACNET_POLARITY polarity)
{
bool status = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
Polarity[index] = polarity;
}
return status;
}
/* return apdu length, or BACNET_STATUS_ERROR on error */
/* assumption - object already exists, and has been bounds checked */
int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
bool state = false;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_INPUT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
/* note: object name must be unique in our device */
Binary_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_BINARY_INPUT);
break;
case PROP_PRESENT_VALUE:
/* note: you need to look up the actual value */
apdu_len = encode_application_enumerated(
&apdu[0], Binary_Input_Present_Value(rpdata->object_instance));
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Binary_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_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Binary_Input_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_POLARITY:
apdu_len = encode_application_enumerated(
&apdu[0], Binary_Input_Polarity(rpdata->object_instance));
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 Binary_Input_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->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_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Enumerated <= MAX_BINARY_PV) {
Binary_Input_Present_Value_Set(wp_data->object_instance,
(BACNET_BINARY_PV)value.type.Enumerated);
} else {
status = false;
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) {
Binary_Input_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_POLARITY:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Enumerated < MAX_POLARITY) {
Binary_Input_Polarity_Set(wp_data->object_instance,
(BACNET_POLARITY)value.type.Enumerated);
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
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 <assert.h>
#include <string.h>
#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 testBinaryInput(Test *pTest)
{
BACNET_READ_PROPERTY_DATA rpdata;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
Binary_Input_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_INPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_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_BINARY_INPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary Input", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinaryInput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_INPUT */
#endif /* TEST */
+135
View File
@@ -0,0 +1,135 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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 BI_H
#define BI_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/cov.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Input_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Input_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Input_Count(
void);
uint32_t Binary_Input_Index_To_Instance(
unsigned index);
unsigned Binary_Input_Instance_To_Index(
uint32_t instance);
bool Binary_Input_Object_Instance_Add(
uint32_t instance);
bool Binary_Input_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Input_Name_Set(
uint32_t object_instance,
char *new_name);
BACNET_BINARY_PV Binary_Input_Present_Value(
uint32_t object_instance);
bool Binary_Input_Present_Value_Set(
uint32_t object_instance,
BACNET_BINARY_PV value);
char *Binary_Input_Description(
uint32_t instance);
bool Binary_Input_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Binary_Input_Reliability(
uint32_t object_instance);
bool Binary_Input_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
char *Binary_Input_Inactive_Text(
uint32_t instance);
bool Binary_Input_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Input_Active_Text(
uint32_t instance);
bool Binary_Input_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_POLARITY Binary_Input_Polarity(
uint32_t object_instance);
bool Binary_Input_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Input_Out_Of_Service(
uint32_t object_instance);
void Binary_Input_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
bool Binary_Input_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Input_Change_Of_Value(
uint32_t instance);
void Binary_Input_Change_Of_Value_Clear(
uint32_t instance);
int Binary_Input_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Input_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Input_Create(
uint32_t object_instance);
bool Binary_Input_Delete(
uint32_t object_instance);
void Binary_Input_Cleanup(
void);
void Binary_Input_Init(
void);
#ifdef TEST
#include "ctest.h"
void testBinaryInput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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 -DTEST_BINARY_INPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bi.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = binary_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
+519
View File
@@ -0,0 +1,519 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Binary Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/bo.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_OUTPUTS
#define MAX_BINARY_OUTPUTS 4
#endif
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define RELINQUISH_DEFAULT BINARY_INACTIVE
/* Here is our Priority Array.*/
static BACNET_BINARY_PV Binary_Output_Level[MAX_BINARY_OUTPUTS]
[BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_BINARY_OUTPUTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, -1 };
static const int Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 };
static const int Binary_Output_Properties_Proprietary[] = { -1 };
void Binary_Output_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Output_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Output_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Output_Properties_Proprietary;
}
return;
}
void Binary_Output_Init(void)
{
unsigned i, j;
static bool initialized = false;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Binary_Output_Level[i][j] = BINARY_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Output_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_OUTPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Binary_Output_Count(void)
{
return MAX_BINARY_OUTPUTS;
}
/* 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 Binary_Output_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 Binary_Output_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_OUTPUTS;
if (object_instance < MAX_BINARY_OUTPUTS) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Binary_Output_Instance_To_Index(object_instance);
if (index < MAX_BINARY_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Binary_Output_Level[index][i] != BINARY_NULL) {
value = Binary_Output_Level[index][i];
break;
}
}
}
return value;
}
bool Binary_Output_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Binary_Output_Instance_To_Index(object_instance);
if (index < MAX_BINARY_OUTPUTS) {
value = Out_Of_Service[index];
}
return value;
}
/* note: the object name must be unique within this device */
bool Binary_Output_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_BINARY_OUTPUTS) {
sprintf(
text_string, "BINARY OUTPUT %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 Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
BACNET_POLARITY polarity = POLARITY_NORMAL;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_OUTPUT, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Binary_Output_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_BINARY_OUTPUT);
break;
case PROP_PRESENT_VALUE:
present_value =
Binary_Output_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
state = Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_POLARITY:
apdu_len = encode_application_enumerated(&apdu[0], polarity);
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) {
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Binary_Output_Level[object_index][i] == BINARY_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Output_Level[object_index][i];
len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Binary_Output_Level[object_index][rpdata->array_index -
1] == BINARY_NULL) {
apdu_len = encode_application_null(&apdu[apdu_len]);
} else {
present_value =
Binary_Output_Level[object_index]
[rpdata->array_index - 1];
apdu_len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
} 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:
present_value = RELINQUISH_DEFAULT;
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_ACTIVE_TEXT:
characterstring_init_ansi(&char_string, "on");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_INACTIVE_TEXT:
characterstring_init_ansi(&char_string, "off");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
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 Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
unsigned int priority = 0;
BACNET_BINARY_PV level = BINARY_NULL;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
priority = wp_data->priority;
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) &&
(value.type.Enumerated <= MAX_BINARY_PV)) {
level = (BACNET_BINARY_PV)value.type.Enumerated;
object_index = Binary_Output_Instance_To_Index(
wp_data->object_instance);
priority--;
Binary_Output_Level[object_index][priority] = level;
/* Note: you could set the physical output here if we
are the highest priority.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing
output) */
status = true;
} else if (priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = BINARY_NULL;
object_index = Binary_Output_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
Binary_Output_Level[object_index][priority] = level;
/* Note: you could set the physical output here to the
next highest priority, or to the relinquish default
if no priorities are set. However, if Out of Service
is TRUE, then don't set the physical output. This
comment may apply to the
main loop (i.e. check out of service before changing
output) */
} else {
status = false;
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) {
object_index =
Binary_Output_Instance_To_Index(wp_data->object_instance);
Out_Of_Service[object_index] = value.type.Boolean;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
case PROP_EVENT_STATE:
case PROP_POLARITY:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
case PROP_ACTIVE_TEXT:
case PROP_INACTIVE_TEXT:
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 <assert.h>
#include <string.h>
#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 testBinaryOutput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Binary_Output_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_OUTPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_Output_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_BINARY_OUTPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary Output", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinaryOutput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_INPUT */
#endif /* TEST */
+141
View File
@@ -0,0 +1,141 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 BO_H
#define BO_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Output_Init(
void);
void Binary_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Output_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Output_Count(
void);
uint32_t Binary_Output_Index_To_Instance(
unsigned index);
unsigned Binary_Output_Instance_To_Index(
uint32_t instance);
bool Binary_Output_Object_Instance_Add(
uint32_t instance);
bool Binary_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Output_Name_Set(
uint32_t object_instance,
char *new_name);
char *Binary_Output_Description(
uint32_t instance);
bool Binary_Output_Description_Set(
uint32_t instance,
char *new_name);
char *Binary_Output_Inactive_Text(
uint32_t instance);
bool Binary_Output_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Output_Active_Text(
uint32_t instance);
bool Binary_Output_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_BINARY_PV Binary_Output_Present_Value(
uint32_t instance);
bool Binary_Output_Present_Value_Set(
uint32_t instance,
BACNET_BINARY_PV binary_value,
unsigned priority);
bool Binary_Output_Present_Value_Relinquish(
uint32_t instance,
unsigned priority);
unsigned Binary_Output_Present_Value_Priority(
uint32_t object_instance);
BACNET_POLARITY Binary_Output_Polarity(
uint32_t instance);
bool Binary_Output_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Output_Out_Of_Service(
uint32_t instance);
void Binary_Output_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
BACNET_BINARY_PV Binary_Output_Relinquish_Default(
uint32_t object_instance);
bool Binary_Output_Relinquish_Default_Set(
uint32_t object_instance,
BACNET_BINARY_PV value);
bool Binary_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Output_Change_Of_Value(
uint32_t instance);
void Binary_Output_Change_Of_Value_Clear(
uint32_t instance);
int Binary_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Output_Create(
uint32_t object_instance);
bool Binary_Output_Delete(
uint32_t object_instance);
void Binary_Output_Cleanup(
void);
#ifdef TEST
#include "ctest.h"
void testBinaryOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_BINARY_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bo.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = binary_output
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
+506
View File
@@ -0,0 +1,506 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Binary Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_VALUES
#define MAX_BINARY_VALUES 10
#endif
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define RELINQUISH_DEFAULT BINARY_INACTIVE
/* Here is our Priority Array.*/
static BACNET_BINARY_PV Binary_Value_Level[MAX_BINARY_VALUES]
[BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_BINARY_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, -1 };
static const int Binary_Value_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, -1 };
static const int Binary_Value_Properties_Proprietary[] = { -1 };
void Binary_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Value_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Value_Properties_Proprietary;
}
return;
}
void Binary_Value_Init(void)
{
unsigned i, j;
static bool initialized = false;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_BINARY_VALUES; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Binary_Value_Level[i][j] = BINARY_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_VALUES) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Binary_Value_Count(void)
{
return MAX_BINARY_VALUES;
}
/* 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 Binary_Value_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 Binary_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_VALUES;
if (object_instance < MAX_BINARY_VALUES) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Binary_Value_Instance_To_Index(object_instance);
if (index < MAX_BINARY_VALUES) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Binary_Value_Level[index][i] != BINARY_NULL) {
value = Binary_Value_Level[index][i];
break;
}
}
}
return value;
}
/* note: the object name must be unique within this device */
bool Binary_Value_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_BINARY_VALUES) {
sprintf(
text_string, "BINARY VALUE %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Binary_Value_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Binary_Value_Instance_To_Index(instance);
if (index < MAX_BINARY_VALUES) {
oos_flag = Out_Of_Service[index];
}
return oos_flag;
}
void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Binary_Value_Instance_To_Index(instance);
if (index < MAX_BINARY_VALUES) {
Out_Of_Service[index] = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_VALUE, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Binary_Value_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_BINARY_VALUE);
break;
case PROP_PRESENT_VALUE:
present_value = Binary_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Binary_Value_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:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Binary_Value_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) {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Binary_Value_Level[object_index][i] == BINARY_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Value_Level[object_index][i];
len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Binary_Value_Level[object_index][rpdata->array_index] ==
BINARY_NULL) {
apdu_len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Value_Level[object_index]
[rpdata->array_index];
apdu_len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
} 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:
present_value = RELINQUISH_DEFAULT;
apdu_len = encode_application_enumerated(&apdu[0], present_value);
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 Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
unsigned int priority = 0;
BACNET_BINARY_PV level = BINARY_NULL;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* 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;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
priority = wp_data->priority;
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) &&
(value.type.Enumerated <= MAX_BINARY_PV)) {
level = (BACNET_BINARY_PV)value.type.Enumerated;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority--;
Binary_Value_Level[object_index][priority] = level;
/* Note: you could set the physical output here if we
are the highest priority.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing
output) */
status = true;
} else if (priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = BINARY_NULL;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
Binary_Value_Level[object_index][priority] = level;
/* Note: you could set the physical output here to the
next highest priority, or to the relinquish default
if no priorities are set. However, if Out of Service
is TRUE, then don't set the physical output. This
comment may apply to the
main loop (i.e. check out of service before changing
output) */
} else {
status = false;
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) {
Binary_Value_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
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 <assert.h>
#include <string.h>
#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 testBinary_Value(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Binary_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_Value_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_BINARY_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary_Value", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinary_Value);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_VALUE */
#endif /* TEST */
+152
View File
@@ -0,0 +1,152 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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 BV_H
#define BV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Value_Init(
void);
void Binary_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Value_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Value_Count(
void);
uint32_t Binary_Value_Index_To_Instance(
unsigned index);
unsigned Binary_Value_Instance_To_Index(
uint32_t object_instance);
bool Binary_Value_Object_Instance_Add(
uint32_t instance);
bool Binary_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Value_Name_Set(
uint32_t object_instance,
char *new_name);
char *Binary_Value_Description(
uint32_t instance);
bool Binary_Value_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Binary_Value_Reliability(
uint32_t object_instance);
bool Binary_Value_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
char *Binary_Value_Inactive_Text(
uint32_t instance);
bool Binary_Value_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Value_Active_Text(
uint32_t instance);
bool Binary_Value_Active_Text_Set(
uint32_t instance,
char *new_name);
int Binary_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Value_Change_Of_Value(
uint32_t instance);
void Binary_Value_Change_Of_Value_Clear(
uint32_t instance);
BACNET_BINARY_PV Binary_Value_Present_Value(
uint32_t instance);
bool Binary_Value_Present_Value_Set(
uint32_t instance,
BACNET_BINARY_PV value);
bool Binary_Value_Out_Of_Service(
uint32_t instance);
void Binary_Value_Out_Of_Service_Set(
uint32_t instance,
bool value);
char *Binary_Value_Description(
uint32_t instance);
bool Binary_Value_Description_Set(
uint32_t object_instance,
char *text_string);
char *Binary_Value_Inactive_Text(
uint32_t instance);
bool Binary_Value_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Value_Active_Text(
uint32_t instance);
bool Binary_Value_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_POLARITY Binary_Value_Polarity(
uint32_t instance);
bool Binary_Value_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Value_Create(
uint32_t object_instance);
bool Binary_Value_Delete(
uint32_t object_instance);
void Binary_Value_Cleanup(
void);
#ifdef TEST
#include "ctest.h"
void testBinary_Value(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_BINARY_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = binary_value
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
File diff suppressed because it is too large Load Diff
+203
View File
@@ -0,0 +1,203 @@
/**
* @file
* @author Steve Karg
* @date 2013
* @brief Channel objects, customize for your use
*
* @section DESCRIPTION
*
* The Channel object is a command object without a priority array, and the
* present-value property uses a priority array and a single precision floating point
* data type.
*
* @section LICENSE
*
* 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 CHANNEL_H
#define CHANNEL_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/lo.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests
Choose the datatypes that your application supports */
#if !(defined(CHANNEL_NUMERIC) || \
defined(CHANNEL_NULL) || \
defined(CHANNEL_BOOLEAN) || \
defined(CHANNEL_UNSIGNED) || \
defined(CHANNEL_SIGNED) || \
defined(CHANNEL_REAL) || \
defined(CHANNEL_DOUBLE) || \
defined(CHANNEL_OCTET_STRING) || \
defined(CHANNEL_CHARACTER_STRING) || \
defined(CHANNEL_BIT_STRING) || \
defined(CHANNEL_ENUMERATED) || \
defined(CHANNEL_DATE) || \
defined(CHANNEL_TIME) || \
defined(CHANNEL_OBJECT_ID) || \
defined(CHANNEL_LIGHTING_COMMAND))
#define CHANNEL_NUMERIC
#endif
#if defined (CHANNEL_NUMERIC)
#define CHANNEL_NULL
#define CHANNEL_BOOLEAN
#define CHANNEL_UNSIGNED
#define CHANNEL_SIGNED
#define CHANNEL_REAL
#define CHANNEL_DOUBLE
#define CHANNEL_ENUMERATED
#define CHANNEL_LIGHTING_COMMAND
#endif
typedef struct BACnet_Channel_Value_t {
uint8_t tag;
union {
/* NULL - not needed as it is encoded in the tag alone */
#if defined (CHANNEL_BOOLEAN)
bool Boolean;
#endif
#if defined (CHANNEL_UNSIGNED)
uint32_t Unsigned_Int;
#endif
#if defined (CHANNEL_SIGNED)
int32_t Signed_Int;
#endif
#if defined (CHANNEL_REAL)
float Real;
#endif
#if defined (CHANNEL_DOUBLE)
double Double;
#endif
#if defined (CHANNEL_OCTET_STRING)
BACNET_OCTET_STRING Octet_String;
#endif
#if defined (CHANNEL_CHARACTER_STRING)
BACNET_CHARACTER_STRING Character_String;
#endif
#if defined (CHANNEL_BIT_STRING)
BACNET_BIT_STRING Bit_String;
#endif
#if defined (CHANNEL_ENUMERATED)
uint32_t Enumerated;
#endif
#if defined (CHANNEL_DATE)
BACNET_DATE Date;
#endif
#if defined (CHANNEL_TIME)
BACNET_TIME Time;
#endif
#if defined (CHANNEL_OBJECT_ID)
BACNET_OBJECT_ID Object_Id;
#endif
#if defined (CHANNEL_LIGHTING_COMMAND)
BACNET_LIGHTING_COMMAND Lighting_Command;
#endif
} type;
/* simple linked list if needed */
struct BACnet_Channel_Value_t *next;
} BACNET_CHANNEL_VALUE;
void Channel_Property_Lists(const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Channel_Valid_Instance(uint32_t object_instance);
unsigned Channel_Count(void);
uint32_t Channel_Index_To_Instance(unsigned index);
unsigned Channel_Instance_To_Index(uint32_t instance);
bool Channel_Object_Instance_Add(uint32_t instance);
bool Channel_Object_Name(uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Channel_Name_Set(uint32_t object_instance,
char *new_name);
int Channel_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata);
bool Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_CHANNEL_VALUE * Channel_Present_Value(uint32_t object_instance);
bool Channel_Present_Value_Set(
BACNET_WRITE_PROPERTY_DATA * wp_data,
BACNET_APPLICATION_DATA_VALUE * value);
bool Channel_Out_Of_Service(uint32_t object_instance);
void Channel_Out_Of_Service_Set(uint32_t object_instance,
bool oos_flag);
unsigned Channel_Last_Priority(uint32_t object_instance);
BACNET_WRITE_STATUS Channel_Write_Status(uint32_t object_instance);
uint16_t Channel_Number(uint32_t object_instance);
bool Channel_Number_Set(uint32_t object_instance, uint16_t value);
unsigned Channel_Reference_List_Member_Count(uint32_t object_instance);
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *
Channel_Reference_List_Member_Element(uint32_t object_instance,
unsigned element);
bool Channel_Reference_List_Member_Element_Set(uint32_t object_instance,
unsigned array_index,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
unsigned Channel_Reference_List_Member_Element_Add(uint32_t object_instance,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
unsigned Channel_Reference_List_Member_Local_Add(
uint32_t object_instance,
uint16_t type,
uint32_t instance,
BACNET_PROPERTY_ID propertyIdentifier,
uint32_t arrayIndex);
uint16_t Channel_Control_Groups_Element(
uint32_t object_instance,
int32_t array_index);
bool Channel_Control_Groups_Element_Set(
uint32_t object_instance,
int32_t array_index,
uint16_t value);
bool Channel_Value_Copy(BACNET_CHANNEL_VALUE * cvalue,
BACNET_APPLICATION_DATA_VALUE * value);
int Channel_Value_Encode(uint8_t *apdu, int apdu_max,
BACNET_CHANNEL_VALUE * value);
int Channel_Coerce_Data_Encode(
uint8_t * apdu,
unsigned max_apdu,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_APPLICATION_TAG tag);
bool Channel_Write_Member_Value(
BACNET_WRITE_PROPERTY_DATA * wp_data,
BACNET_APPLICATION_DATA_VALUE * value);
void Channel_Init(void);
#ifdef TEST
#include "ctest.h"
void testChannelObject(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
File diff suppressed because it is too large Load Diff
+918
View File
@@ -0,0 +1,918 @@
/**
* @file
* @author Nikola Jelic
* @date 2014
* @brief Command objects, customize for your use
*
* @section DESCRIPTION
*
* The Command object type defines a standardized object whose
* properties represent the externally visible characteristics of a
* multi-action command procedure. A Command object is used to
* write a set of values to a group of object properties, based on
* the "action code" that is written to the Present_Value of the
* Command object. Whenever the Present_Value property of the
* Command object is written to, it triggers the Command object
* to take a set of actions that change the values of a set of other
* objects' properties.
*
* @section LICENSE
*
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/proplist.h"
#include "bacnet/timestamp.h"
#include "bacnet/basic/object/command.h"
/*BACnetActionCommand ::= SEQUENCE {
deviceIdentifier [0] BACnetObjectIdentifier OPTIONAL,
objectIdentifier [1] BACnetObjectIdentifier,
propertyIdentifier [2] BACnetPropertyIdentifier,
propertyArrayIndex [3] Unsigned OPTIONAL, --used only with array datatype
propertyValue [4] ABSTRACT-SYNTAX.&Type,
priority [5] Unsigned (1..16) OPTIONAL, --used only when property is commandable
postDelay [6] Unsigned OPTIONAL,
quitOnFailure [7] BOOLEAN,
writeSuccessful [8] BOOLEAN
}*/
int cl_encode_apdu(uint8_t *apdu, BACNET_ACTION_LIST *bcl)
{
int len = 0;
int apdu_len = 0;
if (bcl->Device_Id.instance >= 0 &&
bcl->Device_Id.instance <= BACNET_MAX_INSTANCE) {
len = encode_context_object_id(
&apdu[apdu_len], 0, bcl->Device_Id.type, bcl->Device_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
/* TODO: Check for object type and instance limits */
len = encode_context_object_id(
&apdu[apdu_len], 1, bcl->Object_Id.type, bcl->Object_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len =
encode_context_enumerated(&apdu[apdu_len], 2, bcl->Property_Identifier);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (bcl->Property_Array_Index != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(
&apdu[apdu_len], 3, bcl->Property_Array_Index);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
/* BACnet Testing Observed Incident oi00108
Command Action not correctly formatted
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00031 BC
135.1: 9.20.1.7 BC 135.1: 9.20.1.9 Any discussions can be directed to
edward@bac-test.com Please feel free to remove this comment when my
changes have been reviewed by all interested parties. Say 6 months ->
September 2016 */
len = encode_opening_tag(&apdu[apdu_len], 4);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len], &bcl->Value);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 4);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (bcl->Priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(&apdu[apdu_len], 5, bcl->Priority);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
if (bcl->Post_Delay != 0xFFFFFFFFU) {
len = encode_context_unsigned(&apdu[apdu_len], 6, bcl->Post_Delay);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
len = encode_context_boolean(&apdu[apdu_len], 7, bcl->Quit_On_Failure);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = encode_context_boolean(&apdu[apdu_len], 8, bcl->Write_Successful);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
return apdu_len;
}
int cl_decode_apdu(uint8_t *apdu,
unsigned apdu_len,
BACNET_APPLICATION_TAG tag,
BACNET_ACTION_LIST *bcl)
{
int len = 0;
int dec_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
if (decode_is_context_tag(&apdu[dec_len], 0)) {
/* Tag 0: Device ID */
dec_len++;
len = decode_object_id(
&apdu[dec_len], &bcl->Device_Id.type, &bcl->Device_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
}
if (!decode_is_context_tag(&apdu[dec_len++], 1)) {
return BACNET_STATUS_REJECT;
}
len = decode_object_id(
&apdu[dec_len], &bcl->Object_Id.type, &bcl->Object_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (tag_number != 2) {
return BACNET_STATUS_REJECT;
}
len = decode_enumerated(
&apdu[dec_len], len_value_type, &bcl->Property_Identifier);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (decode_is_context_tag(&apdu[dec_len], 3)) {
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(
&apdu[dec_len], len_value_type, &bcl->Property_Array_Index);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
} else {
bcl->Property_Array_Index = BACNET_ARRAY_ALL;
}
if (!decode_is_context_tag(&apdu[dec_len], 4)) {
return BACNET_STATUS_REJECT;
}
bcl->Value.context_specific = true;
bcl->Value.context_tag = 4;
bcl->Value.tag = tag;
switch (tag) {
case BACNET_APPLICATION_TAG_NULL:
len = 1;
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
len = decode_context_boolean2(
&apdu[dec_len], 4, &bcl->Value.type.Boolean);
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
len = decode_context_unsigned(
&apdu[dec_len], 4, &bcl->Value.type.Unsigned_Int);
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
len = decode_context_signed(
&apdu[dec_len], 4, &bcl->Value.type.Signed_Int);
break;
case BACNET_APPLICATION_TAG_REAL:
len = decode_context_real(&apdu[dec_len], 4, &bcl->Value.type.Real);
break;
case BACNET_APPLICATION_TAG_DOUBLE:
len = decode_context_double(
&apdu[dec_len], 4, &bcl->Value.type.Double);
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
len = decode_context_octet_string(
&apdu[dec_len], 4, &bcl->Value.type.Octet_String);
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
len = decode_context_character_string(
&apdu[dec_len], 4, &bcl->Value.type.Character_String);
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
len = decode_context_bitstring(
&apdu[dec_len], 4, &bcl->Value.type.Bit_String);
break;
case BACNET_APPLICATION_TAG_ENUMERATED:
len = decode_context_enumerated(
&apdu[dec_len], 4, &bcl->Value.type.Enumerated);
break;
case BACNET_APPLICATION_TAG_DATE:
len = decode_context_date(&apdu[dec_len], 4, &bcl->Value.type.Date);
break;
case BACNET_APPLICATION_TAG_TIME:
len = decode_context_bacnet_time(
&apdu[dec_len], 4, &bcl->Value.type.Time);
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
len = decode_context_object_id(&apdu[dec_len], 4,
&bcl->Value.type.Object_Id.type,
&bcl->Value.type.Object_Id.instance);
break;
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
len = lighting_command_decode(&apdu[dec_len], apdu_len - dec_len,
&bcl->Value.type.Lighting_Command);
break;
default:
return BACNET_STATUS_REJECT;
break;
}
if (len > 0) {
dec_len += len;
}
if (decode_is_context_tag(&apdu[dec_len], 5)) {
uint32_t priority_dec;
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(&apdu[dec_len], len_value_type, &priority_dec);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
bcl->Priority = (uint8_t)priority_dec;
dec_len += len;
} else {
bcl->Priority = BACNET_NO_PRIORITY;
}
if (decode_is_context_tag(&apdu[dec_len], 6)) {
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(&apdu[dec_len], len_value_type, &bcl->Post_Delay);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
} else {
bcl->Post_Delay = 0xFFFFFFFFU;
}
if (!decode_is_context_tag(&apdu[dec_len], 7)) {
return BACNET_STATUS_REJECT;
}
len = decode_context_boolean2(&apdu[dec_len], 7, &bcl->Quit_On_Failure);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (!decode_is_context_tag(&apdu[dec_len], 8)) {
return BACNET_STATUS_REJECT;
}
len = decode_context_boolean2(&apdu[dec_len], 8, &bcl->Write_Successful);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (dec_len < apdu_len) {
return BACNET_STATUS_REJECT;
}
return dec_len;
}
COMMAND_DESCR Command_Descr[MAX_COMMANDS];
/* These arrays are used by the ReadPropertyMultiple handler */
static const int Command_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_IN_PROCESS,
PROP_ALL_WRITES_SUCCESSFUL, PROP_ACTION, -1 };
static const int Command_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Command_Properties_Proprietary[] = { -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Command_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Command_Properties_Required;
}
if (pOptional) {
*pOptional = Command_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Command_Properties_Proprietary;
}
return;
}
/**
* Initializes the Command object data
*/
void Command_Init(void)
{
unsigned i;
for (i = 0; i < MAX_COMMANDS; i++) {
Command_Descr[i].Present_Value = 0;
Command_Descr[i].In_Process = false;
Command_Descr[i].All_Writes_Successful = true; /* Optimistic default */
}
}
/**
* Determines if a given object instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Command_Valid_Instance(uint32_t object_instance)
{
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
return true;
}
return false;
}
/**
* Determines the number of this object instances
*
* @return Number of objects
*/
unsigned Command_Count(void)
{
return MAX_COMMANDS;
}
/**
* Determines the object instance-number for a given 0..N index
* of objects where N is the total number of this object instances.
*
* @param index - 0..total number of this object instances
*
* @return object instance-number for the given index
*/
uint32_t Command_Index_To_Instance(unsigned index)
{
return index;
}
/**
* For a given object instance-number, determines a 0..N index
* of this object where N is the total number of this object instances
*
* @param object_instance - object-instance number of the object.
*
* @return index for the given instance-number, or
* the total number of this object instances if not valid.
*/
unsigned Command_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_COMMANDS;
if (object_instance < MAX_COMMANDS) {
index = object_instance;
}
return index;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
uint32_t Command_Present_Value(uint32_t object_instance)
{
uint32_t value = 0;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].Present_Value;
}
return value;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - present-value to set
*
* @return true if values are within range and present-value is set.
*/
bool Command_Present_Value_Set(uint32_t object_instance, uint32_t value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].Present_Value = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, determines if the command
* is in-process. This true value indicates that the Command object has
* begun processing one of a set of action sequences. Once all of the
* writes have been attempted by the Command object, the In_Process
* property shall be set back to false.
*
* @param object_instance - object-instance number of the object
*
* @return true if this object-instance is in-process.
*/
bool Command_In_Process(uint32_t object_instance)
{
bool value = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].In_Process;
}
return value;
}
/**
* For a given object instance-number, sets the command in-process value.
*
* @param object_instance - object-instance number of the object
* @param value - true or false value to set
*
* @return true if values are within range and in-process flag is set.
*/
bool Command_In_Process_Set(uint32_t object_instance, bool value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].In_Process = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, indicates the success or failure
* of the sequence of actions that are triggered when the Present_Value
* property is written to.
*
* @param object_instance - object-instance number of the object
*
* @return true if all writes were successful for this object-instance
*/
bool Command_All_Writes_Successful(uint32_t object_instance)
{
bool value = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].All_Writes_Successful;
}
return value;
}
/**
* For a given object instance-number, sets the all-writes-successful value.
*
* @param object_instance - object-instance number of the object
* @param value - true or false value to set
*
* @return true if values are within range and all-writes-succcessful is set.
*/
bool Command_All_Writes_Successful_Set(uint32_t object_instance, bool value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].All_Writes_Successful = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Command_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
unsigned int index;
bool status = false;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
sprintf(text_string, "COMMAND %lu", (unsigned long)index);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Command_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
int len = 0;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
uint8_t *apdu = NULL;
uint16_t apdu_max = 0;
COMMAND_DESCR *CurrentCommand;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu_max = rpdata->application_data_len;
object_index = Command_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_COMMANDS) {
CurrentCommand = &Command_Descr[object_index];
} else {
return false;
}
apdu = rpdata->application_data;
switch ((int)rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_COMMAND, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Command_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_COMMAND);
break;
case PROP_PRESENT_VALUE:
apdu_len = encode_application_unsigned(
&apdu[0], Command_Present_Value(rpdata->object_instance));
break;
case PROP_IN_PROCESS:
apdu_len = encode_application_boolean(
&apdu[0], Command_In_Process(rpdata->object_instance));
break;
case PROP_ALL_WRITES_SUCCESSFUL:
apdu_len = encode_application_boolean(&apdu[0],
Command_All_Writes_Successful(rpdata->object_instance));
break;
case PROP_ACTION:
/* TODO */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], MAX_COMMAND_ACTIONS);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
int i;
for (i = 0; i < MAX_COMMAND_ACTIONS; i++) {
BACNET_ACTION_LIST *Curr_CL_Member =
&CurrentCommand->Action[0];
/* another loop, for aditional actions in the list */
for (; Curr_CL_Member != NULL;
Curr_CL_Member = Curr_CL_Member->next) {
len = cl_encode_apdu(
&apdu[apdu_len], &CurrentCommand->Action[0]);
apdu_len += len;
/* assume the next one is of the same length, which need
* not be the case */
if ((i != MAX_COMMAND_ACTIONS - 1) &&
(apdu_len + len) >= apdu_max) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
}
} else {
if (rpdata->array_index < MAX_COMMAND_ACTIONS) {
BACNET_ACTION_LIST *Curr_CL_Member =
&CurrentCommand->Action[rpdata->array_index];
/* another loop, for aditional actions in the list */
for (; Curr_CL_Member != NULL;
Curr_CL_Member = Curr_CL_Member->next) {
len = cl_encode_apdu(
&apdu[apdu_len], &CurrentCommand->Action[0]);
apdu_len += len;
/* assume the next one is of the same length, which need
* not be the case */
if ((apdu_len + len) >= apdu_max) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} 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_ACTION) &&
(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;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Command_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
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_ACTION) &&
(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 = Command_Instance_To_Index(wp_data->object_instance);
if (object_index >= MAX_COMMANDS) {
return false;
}
switch ((int)wp_data->object_property) {
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Unsigned_Int >= MAX_COMMAND_ACTIONS) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
Command_Present_Value_Set(
wp_data->object_instance, value.type.Unsigned_Int);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
status = false;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_IN_PROCESS:
case PROP_ALL_WRITES_SUCCESSFUL:
case PROP_ACTION:
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;
}
void Command_Intrinsic_Reporting(uint32_t object_instance)
{
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
bool bResult;
/*
* start out assuming success and only set up error
* response if validation fails.
*/
bResult = true;
if (pValue->tag != ucExpectedTag) {
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
}
return (bResult);
}
void testCommand(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;
BACNET_ACTION_LIST clist, clist_test;
Command_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_COMMAND;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Command_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);
memset(&clist, 0, sizeof(BACNET_ACTION_LIST));
memset(&clist_test, 0, sizeof(BACNET_ACTION_LIST));
clist.Device_Id.type = OBJECT_DEVICE;
clist.Device_Id.instance = 3389;
clist.Object_Id.type = OBJECT_ANALOG_VALUE;
clist.Object_Id.instance = 42;
clist.Property_Identifier = PROP_PRESENT_VALUE;
clist.Property_Array_Index = BACNET_ARRAY_ALL;
clist.Value.tag = BACNET_APPLICATION_TAG_REAL;
clist.Value.type.Real = 39.0f;
clist.Priority = 4;
clist.Post_Delay = 0xFFFFFFFFU;
clist.Quit_On_Failure = true;
clist.Write_Successful = false;
clist.next = NULL;
len = cl_encode_apdu(apdu, &clist);
ct_test(pTest, len > 0);
len = cl_decode_apdu(apdu, len, BACNET_APPLICATION_TAG_REAL, &clist_test);
ct_test(pTest, len > 0);
ct_test(pTest, clist.Device_Id.type == clist_test.Device_Id.type);
ct_test(pTest, clist.Device_Id.instance == clist_test.Device_Id.instance);
ct_test(pTest, clist.Object_Id.type == clist_test.Object_Id.type);
ct_test(pTest, clist.Object_Id.instance == clist_test.Object_Id.instance);
ct_test(pTest, clist.Property_Identifier == clist_test.Property_Identifier);
ct_test(
pTest, clist.Property_Array_Index == clist_test.Property_Array_Index);
ct_test(pTest, clist.Value.tag == clist_test.Value.tag);
ct_test(pTest, clist.Value.type.Real == clist_test.Value.type.Real);
ct_test(pTest, clist.Priority == clist_test.Priority);
ct_test(pTest, clist.Post_Delay == clist_test.Post_Delay);
ct_test(pTest, clist.Quit_On_Failure == clist_test.Quit_On_Failure);
ct_test(pTest, clist.Write_Successful == clist_test.Write_Successful);
return;
}
#ifdef TEST_COMMAND
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Command", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCommand);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_COMMAND */
#endif /* TEST */
+175
View File
@@ -0,0 +1,175 @@
/**
* @file
* @author Nikola Jelic
* @date 2014
* @brief Command objects, customize for your use
*
* @section DESCRIPTION
*
* The Command object type defines a standardized object whose
* properties represent the externally visible characteristics of a
* multi-action command procedure. A Command object is used to
* write a set of values to a group of object properties, based on
* the "action code" that is written to the Present_Value of the
* Command object. Whenever the Present_Value property of the
* Command object is written to, it triggers the Command object
* to take a set of actions that change the values of a set of other
* objects' properties.
*
* @section LICENSE
*
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 COMMAND_H
#define COMMAND_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_COMMANDS
#define MAX_COMMANDS 4
#endif
#ifndef MAX_COMMAND_ACTIONS
#define MAX_COMMAND_ACTIONS 8
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct bacnet_action_list {
BACNET_OBJECT_ID Device_Id; /* Optional */
BACNET_OBJECT_ID Object_Id;
BACNET_PROPERTY_ID Property_Identifier;
uint32_t Property_Array_Index; /* Conditional */
BACNET_APPLICATION_DATA_VALUE Value;
uint8_t Priority; /* Conditional */
uint32_t Post_Delay; /* Optional */
bool Quit_On_Failure;
bool Write_Successful;
struct bacnet_action_list *next;
} BACNET_ACTION_LIST;
int cl_encode_apdu(
uint8_t * apdu,
BACNET_ACTION_LIST * bcl);
int cl_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_APPLICATION_TAG tag,
BACNET_ACTION_LIST * bcl);
typedef struct command_descr {
uint32_t Present_Value;
bool In_Process;
bool All_Writes_Successful;
BACNET_ACTION_LIST Action[MAX_COMMAND_ACTIONS];
} COMMAND_DESCR;
void Command_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Command_Valid_Instance(
uint32_t object_instance);
unsigned Command_Count(
void);
uint32_t Command_Index_To_Instance(
unsigned index);
unsigned Command_Instance_To_Index(
uint32_t instance);
bool Command_Object_Instance_Add(
uint32_t instance);
bool Command_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Command_Name_Set(
uint32_t object_instance,
char *new_name);
char *Command_Description(
uint32_t instance);
bool Command_Description_Set(
uint32_t instance,
char *new_name);
int Command_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Command_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
uint32_t Command_Present_Value(
uint32_t object_instance);
bool Command_Present_Value_Set(
uint32_t object_instance,
uint32_t value);
bool Command_In_Process(
uint32_t object_instance);
bool Command_In_Process_Set(
uint32_t object_instance,
bool value);
bool Command_All_Writes_Successful(
uint32_t object_instance);
bool Command_All_Writes_Successful_Set(
uint32_t object_instance,
bool value);
bool Command_Change_Of_Value(
uint32_t instance);
void Command_Change_Of_Value_Clear(
uint32_t instance);
bool Command_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Command_COV_Increment(
uint32_t instance);
void Command_COV_Increment_Set(
uint32_t instance,
float value);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Command_Intrinsic_Reporting(
uint32_t object_instance);
void Command_Init(
void);
#ifdef TEST
#include "ctest.h"
void testCommand(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -0,0 +1,43 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
HANDLER_DIR = ../handler
INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR)
DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_COMMAND
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = command.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = command
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
@@ -0,0 +1,431 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/basic/object/credential_data_input.h"
#include "bacnet/basic/services.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_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_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
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 <assert.h>
#include <string.h>
#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 */
@@ -0,0 +1,116 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/timestamp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/authentication_factor.h"
#include "bacnet/authentication_factor_format.h"
#include "bacnet/timestamp.h"
#include "bacnet/rp.h"
#include "bacnet/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
@@ -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)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/authentication_factor.c \
$(SRC_DIR)/bacnet/authentication_factor_format.c \
$(SRC_DIR)/bacnet/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
+481
View File
@@ -0,0 +1,481 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* CharacterString Value Objects */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/csv.h"
#include "bacnet/basic/services.h"
/* number of demo objects */
#ifndef MAX_CHARACTERSTRING_VALUES
#define MAX_CHARACTERSTRING_VALUES 1
#endif
/* Here is our Present Value */
static BACNET_CHARACTER_STRING Present_Value[MAX_CHARACTERSTRING_VALUES];
/* Writable out-of-service allows others to manipulate our Present Value */
static bool Out_Of_Service[MAX_CHARACTERSTRING_VALUES];
static char Object_Name[MAX_CHARACTERSTRING_VALUES][64];
static char Object_Description[MAX_CHARACTERSTRING_VALUES][64];
/* 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,
-1 };
static const int Properties_Optional[] = { PROP_EVENT_STATE,
PROP_OUT_OF_SERVICE, PROP_DESCRIPTION, -1 };
static const int Properties_Proprietary[] = { -1 };
void CharacterString_Value_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 CharacterString_Value_Init(void)
{
unsigned i;
/* initialize all Present Values */
for (i = 0; i < MAX_CHARACTERSTRING_VALUES; i++) {
snprintf(&Object_Name[i][0], sizeof(Object_Name[i]),
"CHARACTER STRING VALUE %u", i + 1);
snprintf(&Object_Description[i][0], sizeof(Object_Description[i]),
"A Character String Value Example");
characterstring_init_ansi(&Present_Value[i], "");
}
return;
}
/* 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 CharacterString_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_CHARACTERSTRING_VALUES;
if (object_instance < MAX_CHARACTERSTRING_VALUES) {
index = object_instance;
}
return index;
}
/* 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 CharacterString_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned CharacterString_Value_Count(void)
{
return MAX_CHARACTERSTRING_VALUES;
}
bool CharacterString_Value_Valid_Instance(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
return true;
}
return false;
}
bool CharacterString_Value_Present_Value(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
unsigned index = 0; /* offset from instance lookup */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (object_name && (index < MAX_CHARACTERSTRING_VALUES)) {
status = characterstring_copy(object_name, &Present_Value[index]);
}
return status;
}
bool CharacterString_Value_Present_Value_Set(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
unsigned index = 0; /* offset from instance lookup */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
status = characterstring_copy(&Present_Value[index], object_name);
}
return status;
}
bool CharacterString_Value_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
value = Out_Of_Service[index];
}
return value;
}
static void CharacterString_Value_Out_Of_Service_Set(
uint32_t object_instance, bool value)
{
unsigned index = 0;
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
Out_Of_Service[index] = value;
}
return;
}
static char *CharacterString_Value_Description(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
char *pName = NULL; /* return value */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
pName = Object_Description[index];
}
return pName;
}
bool CharacterString_Value_Description_Set(
uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
status = true;
if (new_name) {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = 0;
}
}
}
return status;
}
bool CharacterString_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
unsigned index = 0; /* offset from instance lookup */
bool status = false;
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
status = characterstring_init_ansi(object_name, Object_Name[index]);
}
return status;
}
/* note: the object name must be unique within this device */
bool CharacterString_Value_Name_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = CharacterString_Value_Instance_To_Index(object_instance);
if (index < MAX_CHARACTERSTRING_VALUES) {
status = true;
/* FIXME: check to see if there is a matching name */
if (new_name) {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = 0;
}
}
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int CharacterString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(&apdu[0],
OBJECT_CHARACTERSTRING_VALUE, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
CharacterString_Value_Object_Name(
rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string,
CharacterString_Value_Description(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], OBJECT_CHARACTERSTRING_VALUE);
break;
case PROP_PRESENT_VALUE:
CharacterString_Value_Present_Value(
rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
if (CharacterString_Value_Out_Of_Service(rpdata->object_instance)) {
bitstring_set_bit(
&bit_string, STATUS_FLAG_OUT_OF_SERVICE, true);
} else {
bitstring_set_bit(
&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
}
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
object_index = CharacterString_Value_Instance_To_Index(
rpdata->object_instance);
state = Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], 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_STATE_TEXT) &&
(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 CharacterString_Value_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;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status = WPValidateArgType(&value,
BACNET_APPLICATION_TAG_CHARACTER_STRING, &wp_data->error_class,
&wp_data->error_code);
if (status) {
status = CharacterString_Value_Present_Value_Set(
wp_data->object_instance, &value.type.Character_String);
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) {
CharacterString_Value_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
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 <assert.h>
#include <string.h>
#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 testCharacterStringValue(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
CharacterString_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_CHARACTERSTRING_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = CharacterString_Value_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_CHARACTERSTRING_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet CharacterString Value", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCharacterStringValue);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
+95
View File
@@ -0,0 +1,95 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* 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 CSV_H
#define CSV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void CharacterString_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool CharacterString_Value_Valid_Instance(
uint32_t object_instance);
unsigned CharacterString_Value_Count(
void);
uint32_t CharacterString_Value_Index_To_Instance(
unsigned index);
unsigned CharacterString_Value_Instance_To_Index(
uint32_t instance);
int CharacterString_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool CharacterString_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
/* optional API */
bool CharacterString_Value_Object_Instance_Add(
uint32_t instance);
bool CharacterString_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool CharacterString_Value_Name_Set(
uint32_t object_instance,
char *new_name);
bool CharacterString_Value_Present_Value(
uint32_t object_instance,
BACNET_CHARACTER_STRING * value);
bool CharacterString_Value_Present_Value_Set(
uint32_t object_instance,
BACNET_CHARACTER_STRING * value);
bool CharacterString_Value_Description_Set(
uint32_t object_instance,
char *text_string);
bool CharacterString_Value_Out_Of_Service(
uint32_t object_instance);
void CharacterString_Value_Init(
void);
#ifdef TEST
#include "ctest.h"
void testCharacterStringValue(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_CHARACTERSTRING_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = csv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = characterstring_value
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
File diff suppressed because it is too large Load Diff
+476
View File
@@ -0,0 +1,476 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/** @file device.h Defines functions for handling all BACnet objects belonging
* to a BACnet device, as well as Device-specific properties. */
#ifndef DEVICE_H
#define DEVICE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/wp.h"
#include "bacnet/rd.h"
#include "bacnet/rp.h"
#include "bacnet/rpm.h"
#include "bacnet/readrange.h"
/** Called so a BACnet object can perform any necessary initialization.
* @ingroup ObjHelpers
*/
typedef void (
*object_init_function) (
void);
/** Counts the number of objects of this type.
* @ingroup ObjHelpers
* @return Count of implemented objects of this type.
*/
typedef unsigned (
*object_count_function) (
void);
/** Maps an object index position to its corresponding BACnet object instance number.
* @ingroup ObjHelpers
* @param index [in] The index of the object, in the array of objects of its type.
* @return The BACnet object instance number to be used in a BACNET_OBJECT_ID.
*/
typedef uint32_t(
*object_index_to_instance_function)
(
unsigned index);
/** Provides the BACnet Object_Name for a given object instance of this type.
* @ingroup ObjHelpers
* @param object_instance [in] The object instance number to be looked up.
* @param object_name [in,out] Pointer to a character_string structure that
* will hold a copy of the object name if this is a valid object_instance.
* @return True if the object_instance is valid and object_name has been
* filled with a copy of the Object's name.
*/
typedef bool(
*object_name_function)
(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
/** Look in the table of objects of this type, and see if this is a valid
* instance number.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @return True if the object instance refers to a valid object of this type.
*/
typedef bool(
*object_valid_instance_function) (
uint32_t object_instance);
/** Helper function to step through an array of objects and find either the
* first one or the next one of a given type. Used to step through an array
* of objects which is not necessarily contiguious for each type i.e. the
* index for the 'n'th object of a given type is not necessarily 'n'.
* @ingroup ObjHelpers
* @param [in] The index of the current object or a value of ~0 to indicate
* start at the beginning.
* @return The index of the next object of the required type or ~0 (all bits
* == 1) to indicate no more objects found.
*/
typedef unsigned (
*object_iterate_function) (
unsigned current_index);
/** Look in the table of objects of this type, and get the COV Value List.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @param [out] The value list
* @return True if the object instance supports this feature, and has changed.
*/
typedef bool(
*object_value_list_function) (
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
/** Look in the table of objects for this instance to see if value changed.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
* @return True if the object instance has changed.
*/
typedef bool(
*object_cov_function) (
uint32_t object_instance);
/** Look in the table of objects for this instance to clear the changed flag.
* @ingroup ObjHelpers
* @param [in] The object instance number to be looked up.
*/
typedef void (
*object_cov_clear_function) (
uint32_t object_instance);
/** Intrinsic Reporting funcionality.
* @ingroup ObjHelpers
* @param [in] Object instance.
*/
typedef void (
*object_intrinsic_reporting_function) (
uint32_t object_instance);
/** Defines the group of object helper functions for any supported Object.
* @ingroup ObjHelpers
* Each Object must provide some implementation of each of these helpers
* in order to properly support the handlers. Eg, the ReadProperty handler
* handler_read_property() relies on the instance of Object_Read_Property
* for each Object type, or configure the function as NULL.
* In both appearance and operation, this group of functions acts like
* they are member functions of a C++ Object base class.
*/
typedef struct object_functions {
BACNET_OBJECT_TYPE Object_Type;
object_init_function Object_Init;
object_count_function Object_Count;
object_index_to_instance_function Object_Index_To_Instance;
object_valid_instance_function Object_Valid_Instance;
object_name_function Object_Name;
read_property_function Object_Read_Property;
write_property_function Object_Write_Property;
rpm_property_lists_function Object_RPM_List;
rr_info_function Object_RR_Info;
object_iterate_function Object_Iterator;
object_value_list_function Object_Value_List;
object_cov_function Object_COV;
object_cov_clear_function Object_COV_Clear;
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
} object_functions_t;
/* String Lengths - excluding any nul terminator */
#define MAX_DEV_NAME_LEN 32
#define MAX_DEV_LOC_LEN 64
#define MAX_DEV_MOD_LEN 32
#define MAX_DEV_VER_LEN 16
#define MAX_DEV_DESC_LEN 64
/** Structure to define the Object Properties common to all Objects. */
typedef struct commonBacObj_s {
/** The BACnet type of this object (ie, what class is this object from?).
* This property, of type BACnetObjectType, indicates membership in a
* particular object type class. Each inherited class will be of one type.
*/
BACNET_OBJECT_TYPE mObject_Type;
/** The instance number for this class instance. */
uint32_t Object_Instance_Number;
/** Object Name; must be unique.
* This property, of type CharacterString, shall represent a name for
* the object that is unique within the BACnet Device that maintains it.
*/
char Object_Name[MAX_DEV_NAME_LEN];
} COMMON_BAC_OBJECT;
/** Structure to define the Properties of Device Objects which distinguish
* one instance from another.
* This structure only defines fields for properties that are unique to
* a given Device object. The rest may be fixed in device.c or hard-coded
* into the read-property encoding.
* This may be useful for implementations which manage multiple Devices,
* eg, a Gateway.
*/
typedef struct devObj_s {
/** The BACnet Device Address for this device; ->len depends on DLL type. */
BACNET_ADDRESS bacDevAddr;
/** Structure for the Object Properties common to all Objects. */
COMMON_BAC_OBJECT bacObj;
/** Device Description. */
char Description[MAX_DEV_DESC_LEN];
/** The upcounter that shows if the Device ID or object structure has changed. */
uint32_t Database_Revision;
} DEVICE_OBJECT_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Device_Init(
object_functions_t * object_table);
bool Device_Reinitialize(
BACNET_REINITIALIZE_DEVICE_DATA * rd_data);
bool Device_Reinitialize_State_Set(BACNET_REINITIALIZED_STATE state);
BACNET_REINITIALIZED_STATE Device_Reinitialized_State(
void);
rr_info_function Device_Objects_RR_Info(
BACNET_OBJECT_TYPE object_type);
void Device_getCurrentDateTime(
BACNET_DATE_TIME * DateTime);
int32_t Device_UTC_Offset(void);
void Device_UTC_Offset_Set(int16_t offset);
bool Device_Daylight_Savings_Status(void);
bool Device_Align_Intervals(void);
bool Device_Align_Intervals_Set(bool flag);
uint32_t Device_Time_Sync_Interval(void);
bool Device_Time_Sync_Interval_Set(uint32_t value);
uint32_t Device_Interval_Offset(void);
bool Device_Interval_Offset_Set(uint32_t value);
void Device_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
void Device_Objects_Property_List(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
struct special_property_list_t *pPropertyList);
/* functions to support COV */
bool Device_Encode_Value_List(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Device_Value_List_Supported(
BACNET_OBJECT_TYPE object_type);
bool Device_COV(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
void Device_COV_Clear(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance);
uint32_t Device_Object_Instance_Number(
void);
bool Device_Set_Object_Instance_Number(
uint32_t object_id);
bool Device_Valid_Object_Instance_Number(
uint32_t object_id);
unsigned Device_Object_List_Count(
void);
bool Device_Object_List_Identifier(
uint32_t array_index,
int *object_type,
uint32_t * instance);
unsigned Device_Count(
void);
uint32_t Device_Index_To_Instance(
unsigned index);
bool Device_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Device_Set_Object_Name(
BACNET_CHARACTER_STRING * object_name);
/* Copy a child object name, given its ID. */
bool Device_Object_Name_Copy(
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Device_Object_Name_ANSI_Init(const char * object_name);
char * Device_Object_Name_ANSI(void);
BACNET_DEVICE_STATUS Device_System_Status(
void);
int Device_Set_System_Status(
BACNET_DEVICE_STATUS status,
bool local);
const char *Device_Vendor_Name(
void);
uint16_t Device_Vendor_Identifier(
void);
void Device_Set_Vendor_Identifier(
uint16_t vendor_id);
const char *Device_Model_Name(
void);
bool Device_Set_Model_Name(
const char *name,
size_t length);
const char *Device_Firmware_Revision(
void);
const char *Device_Application_Software_Version(
void);
bool Device_Set_Application_Software_Version(
const char *name,
size_t length);
const char *Device_Description(
void);
bool Device_Set_Description(
const char *name,
size_t length);
const char *Device_Location(
void);
bool Device_Set_Location(
const char *name,
size_t length);
/* some stack-centric constant values - no set methods */
uint8_t Device_Protocol_Version(
void);
uint8_t Device_Protocol_Revision(
void);
BACNET_SEGMENTATION Device_Segmentation_Supported(
void);
uint32_t Device_Database_Revision(
void);
void Device_Set_Database_Revision(
uint32_t revision);
void Device_Inc_Database_Revision(
void);
bool Device_Valid_Object_Name(
BACNET_CHARACTER_STRING * object_name,
int *object_type,
uint32_t * object_instance);
bool Device_Valid_Object_Id(
int object_type,
uint32_t object_instance);
int Device_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Device_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool DeviceGetRRInfo(
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
RR_PROP_INFO * pInfo); /* Where to put the information */
int Device_Read_Property_Local(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Device_Write_Property_Local(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#if defined(INTRINSIC_REPORTING)
void Device_local_reporting(
void);
#endif
/* Prototypes for Routing functionality in the Device Object.
* Enable by defining BAC_ROUTING in config.h and including gw_device.c
* in the build (lib/Makefile).
*/
void Routing_Device_Init(
uint32_t first_object_instance);
uint16_t Add_Routed_Device(
uint32_t Object_Instance,
BACNET_CHARACTER_STRING * Object_Name,
const char *Description);
DEVICE_OBJECT_DATA *Get_Routed_Device_Object(
int idx);
BACNET_ADDRESS *Get_Routed_Device_Address(
int idx);
void routed_get_my_address(
BACNET_ADDRESS * my_address);
bool Routed_Device_Address_Lookup(
int idx,
uint8_t address_len,
uint8_t * mac_adress);
bool Routed_Device_GetNext(
BACNET_ADDRESS * dest,
int *DNET_list,
int *cursor);
bool Routed_Device_Is_Valid_Network(
uint16_t dest_net,
int *DNET_list);
uint32_t Routed_Device_Index_To_Instance(
unsigned index);
bool Routed_Device_Valid_Object_Instance_Number(
uint32_t object_id);
bool Routed_Device_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
uint32_t Routed_Device_Object_Instance_Number(
void);
bool Routed_Device_Set_Object_Instance_Number(
uint32_t object_id);
bool Routed_Device_Set_Object_Name(
uint8_t encoding,
const char *value,
size_t length);
bool Routed_Device_Set_Description(
const char *name,
size_t length);
void Routed_Device_Inc_Database_Revision(
void);
int Routed_Device_Service_Approval(
BACNET_CONFIRMED_SERVICE service,
int service_argument,
uint8_t * apdu_buff,
uint8_t invoke_id);
#ifdef __cplusplus
}
#endif /* __cplusplus */
/** @defgroup ObjFrmwk Object Framework
* The modules in this section describe the BACnet-stack's framework for
* BACnet-defined Objects (Device, Analog Input, etc). There are two submodules
* to describe this arrangement:
* - The "object helper functions" which provide C++-like common functionality
* to all supported object types.
* - The interface between the implemented Objects and the BAC-stack services,
* specifically the handlers, which are mediated through function calls to
* the Device object.
*//** @defgroup ObjHelpers Object Helper Functions
* @ingroup ObjFrmwk
* This section describes the function templates for the helper functions that
* provide common object support.
*//** @defgroup ObjIntf Handler-to-Object Interface Functions
* @ingroup ObjFrmwk
* This section describes the fairly limited set of functions that link the
* BAC-stack handlers to the BACnet Object instances. All of these calls are
* situated in the Device Object, which "knows" how to reach its child Objects.
*
* Most of these calls have a common operation:
* -# Call Device_Objects_Find_Functions( for the desired Object_Type )
* - Gets a pointer to the object_functions for this Type of Object.
* -# Call the Object's Object_Valid_Instance( for the desired object_instance )
* to make sure there is such an instance.
* -# Call the Object helper function needed by the handler,
* eg Object_Read_Property() for the RP handler.
*
*/
#endif
+54
View File
@@ -0,0 +1,54 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
PORTS_DIR = ../../ports/linux
INCLUDES = -I../../include -I$(TEST_DIR) -I$(PORTS_DIR) -I.
DEFINES = -DBIG_ENDIAN=0
DEFINES += -DTEST -DBACDL_TEST
DEFINES += -DBACAPP_ALL
DEFINES += -DMAX_TSM_TRANSACTIONS=0
DEFINES += -DTEST_DEVICE
DEFINES += -DBACNET_PROPERTY_LISTS=1
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = device.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/proplist.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/basic/service/h_apdu.c \
$(SRC_DIR)/bacnet/address.c \
$(SRC_DIR)/bacnet/bacaddr.c \
$(SRC_DIR)/bacnet/dcc.c \
$(SRC_DIR)/bacnet/version.c \
$(TEST_DIR)/ctest.c
TARGET = device
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
+630
View File
@@ -0,0 +1,630 @@
/**************************************************************************
*
* Copyright (C) 2005,2010 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/** @file gw_device.c Functions that extend the Device object to support
* routing. */
#include <stdbool.h>
#include <stdint.h>
#include <string.h> /* for memmove */
#include <time.h> /* for timezone, localtime */
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/apdu.h"
#include "bacnet/wp.h" /* write property handling */
#include "bacnet/rp.h" /* read property handling */
#include "bacnet/version.h"
#include "bacnet/basic/object/device.h" /* me */
#include "bacnet/basic/services.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/binding/address.h"
#include "reject.h"
/* include the objects */
#include "bacnet/basic/object/ai.h"
#include "bacnet/basic/object/ao.h"
#include "bacnet/basic/object/av.h"
#include "bacnet/basic/object/bi.h"
#include "bacnet/basic/object/bo.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/object/lc.h"
#include "bacnet/basic/object/lsp.h"
#include "bacnet/basic/object/mso.h"
#include "bacnet/basic/object/ms-input.h"
#include "bacnet/basic/object/trendlog.h"
#if defined(BACFILE)
#include "bacnet/basic/object/bacfile.h" /* object list dependency */
#endif
/* os specfic includes */
#include "bacnet/basic/sys/mstimer.h"
#if defined(__BORLANDC__) || defined(_WIN32)
/* seems to not be defined in time.h as specified by The Open Group */
/* difference from UTC and local standard time */
long int timezone;
#endif
/* local forward and external prototypes */
extern int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
extern bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data);
int Routed_Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata);
bool Routed_Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data);
#if !defined(BAC_ROUTING)
#ifdef _MSC_VER
#pragma message This file should not be included in the build unless \
BAC_ROUTING is enabled.
#else
#warning This file should not be included in the build unless BAC_ROUTING is enabled.
#endif
#endif
/****************************************************************************
************* BACnet Routing Functionality (Optional) **********************
****************************************************************************
* It would be correct to view the routing functionality here as inheriting
* and extending the regular Device Object functionality.
****************************************************************************/
/** Model the gateway as the main Device, with (two) remote
* Devices that are reached via its routing capabilities.
*/
DEVICE_OBJECT_DATA Devices[MAX_NUM_DEVICES];
/** Keep track of the number of managed devices, including the gateway */
uint16_t Num_Managed_Devices = 0;
/** Which Device entry are we currently managing.
* Since we are not using actual class objects here, the best we can do is
* keep this local variable which notes which of the Devices the current
* request is addressing. Should default to 0, the main gateway Device.
*/
uint16_t iCurrent_Device_Idx = 0;
/* void Routing_Device_Init(uint32_t first_object_instance) is
* found in device.c
*/
/** Add a Device to our table of Devices[].
* The first entry must be the gateway device.
* @param Object_Instance [in] Set the new Device to this instance number.
* @param sObject_Name [in] Use this Object Name for the Device.
* @param sDescription [in] Set this Description for the Device.
* @return The index of this instance in the Devices[] array,
* or -1 if there isn't enough room to add this Device.
*/
uint16_t Add_Routed_Device(uint32_t Object_Instance,
BACNET_CHARACTER_STRING *sObject_Name,
const char *sDescription)
{
int i = Num_Managed_Devices;
if (i < MAX_NUM_DEVICES) {
DEVICE_OBJECT_DATA *pDev = &Devices[i];
Num_Managed_Devices++;
iCurrent_Device_Idx = i;
pDev->bacObj.mObject_Type = OBJECT_DEVICE;
pDev->bacObj.Object_Instance_Number = Object_Instance;
if (sObject_Name != NULL) {
Routed_Device_Set_Object_Name(sObject_Name->encoding,
sObject_Name->value, sObject_Name->length);
} else {
Routed_Device_Set_Object_Name(
CHARACTER_UTF8, "No Name", strlen("No Name"));
}
if (sDescription != NULL) {
Routed_Device_Set_Description(sDescription, strlen(sDescription));
} else {
Routed_Device_Set_Description("No Descr", strlen("No Descr"));
}
pDev->Database_Revision = 0; /* Reset/Initialize now */
return i;
} else {
return -1;
}
}
/** Return the Device Object descriptive data for the indicated entry.
* @param idx [in] Index into Devices[] array being requested.
* 0 is for the main, gateway Device entry.
* -1 is a special case meaning "whichever iCurrent_Device_Idx
* is currently set to"
* If valid idx, will set iCurrent_Device_Idx with the idx
* @return Pointer to the requested Device Object data, or NULL if the idx
* is for an invalid row entry (eg, after the last good Device).
*/
DEVICE_OBJECT_DATA *Get_Routed_Device_Object(int idx)
{
if (idx == -1) {
return &Devices[iCurrent_Device_Idx];
} else if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) {
iCurrent_Device_Idx = idx;
return &Devices[idx];
} else {
return NULL;
}
}
/** Return the BACnet address for the indicated entry.
* @param idx [in] Index into Devices[] array being requested.
* 0 is for the main, gateway Device entry.
* -1 is a special case meaning "whichever iCurrent_Device_Idx
* is currently set to"
* If valid idx, will set iCurrent_Device_Idx with the idx
* @return Pointer to the requested Device Object BACnet address, or NULL if the
* idx is for an invalid row entry (eg, after the last good Device).
*/
BACNET_ADDRESS *Get_Routed_Device_Address(int idx)
{
if (idx == -1) {
return &Devices[iCurrent_Device_Idx].bacDevAddr;
} else if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) {
iCurrent_Device_Idx = idx;
return &Devices[idx].bacDevAddr;
} else {
return NULL;
}
}
/** Get the currently active BACnet address.
* This is an implementation of the datalink_get_my_address() template for
* devices with routing.
*
* @param my_address [out] Points to the currently active Device Object's
* BACnet address.
*/
void routed_get_my_address(BACNET_ADDRESS *my_address)
{
if (my_address) {
memcpy(my_address, &Devices[iCurrent_Device_Idx].bacDevAddr,
sizeof(BACNET_ADDRESS));
}
}
/** See if the Gateway or Routed Device at the given idx matches
* the given MAC address.
* Has the desirable side-effect of setting iCurrent_Device_Idx to the
* given idx if a match is found, for use in the subsequent routing handling
* functions here.
*
* @param idx [in] Index into Devices[] array being requested.
* 0 is for the main, gateway Device entry.
* @param address_len [in] Length of the mac_adress[] field.
* If 0, then this is a MAC broadcast. Otherwise, size is determined
* by the DLL type (eg, 6 for BIP and 2 for MSTP).
* @param mac_adress [in] The desired MAC address of a Device;
*
* @return True if the MAC addresses match (or the address_len is 0,
* meaning MAC broadcast, so it's an automatic match).
* Else False if no match or invalid idx is given.
*/
bool Routed_Device_Address_Lookup(
int idx, uint8_t address_len, uint8_t *mac_adress)
{
bool result = false;
DEVICE_OBJECT_DATA *pDev = &Devices[idx];
int i;
if ((idx >= 0) && (idx < MAX_NUM_DEVICES)) {
if (address_len == 0) {
/* Automatic match */
iCurrent_Device_Idx = idx;
result = true;
} else if (mac_adress != NULL) {
for (i = 0; i < address_len; i++) {
if (pDev->bacDevAddr.mac[i] != mac_adress[i]) {
break;
}
}
if (i == address_len) { /* Success! */
iCurrent_Device_Idx = idx;
result = true;
}
}
}
return result;
}
/** Find the next Gateway or Routed Device at the given MAC address,
* starting the search at the "cursor".
* Has the desirable side-effect of setting internal iCurrent_Device_Idx
* if a match is found, for use in the subsequent routing handling
* functions.
*
* @param dest [in] The BACNET_ADDRESS of the message's destination.
* If the Length of the mac_adress[] field is 0, then this is a
* MAC broadcast. Otherwise, size is determined by the DLL type (eg, 6 for BIP
* and 2 for MSTP).
* @param DNET_list [in] List of our reachable downstream BACnet Network
* numbers. Normally just one valid entry; terminated with a -1 value.
* @param cursor [in,out] The concept of the cursor is that it is a starting
* "hint" for the search; on return, it is updated to provide
* the cursor value to use with a subsequent GetNext call, or it equals -1 if
* there are no further matches. Set it to 0 on entry to access the main,
* gateway Device entry, or to start looping through the routed devices.
* Otherwise, its returned value is implementation-dependent and the
* calling function should not alter or interpret it.
*
* @return True if the MAC addresses match (or if BACNET_BROADCAST_NETWORK and
* the dest->len is 0, meaning MAC bcast, so it's an automatic
* match). Else False if no match or invalid idx is given; the cursor will be
* returned as -1 in these cases.
*/
bool Routed_Device_GetNext(BACNET_ADDRESS *dest, int *DNET_list, int *cursor)
{
int dnet = DNET_list[0]; /* Get the DNET of our virtual network */
int idx = *cursor;
bool bSuccess = false;
/* First, see if the index is out of range.
* Eg, last call to GetNext may have been the last successful one.
*/
if ((idx < 0) || (idx >= MAX_NUM_DEVICES)) {
idx = -1;
/* Next, see if it's a BACnet broadcast.
* For broadcasts, all Devices get a chance at it.
*/
} else if (dest->net == BACNET_BROADCAST_NETWORK) {
/* Just take the entry indexed by the cursor */
bSuccess = Routed_Device_Address_Lookup(idx++, dest->len, dest->adr);
}
/* Or see if it's for the main Gateway Device, because
* there's no routing info.
*/
else if (dest->net == 0) {
/* Handle like a normal, non-routed access of the Gateway Device.
* But first, make sure our internal access is pointing at
* that Device in our table by telling it "no routing info" : */
bSuccess = Routed_Device_Address_Lookup(0, dest->len, dest->adr);
/* Next step: no more matches: */
idx = -1;
}
/* Or if is our virtual DNET, check
* against each of our virtually routed Devices.
* If we get a match, have it handle the APDU.
* For broadcasts, all Devices get a chance at it.
*/
else if (dest->net == dnet) {
if (idx == 0) { /* Step over this case (starting point) */
idx = 1;
}
while (idx < MAX_NUM_DEVICES) {
bSuccess =
Routed_Device_Address_Lookup(idx++, dest->len, dest->adr);
if (bSuccess) {
break; /* We don't need to keep looking */
}
}
}
if (!bSuccess) {
*cursor = -1;
} else if (idx == MAX_NUM_DEVICES) { /* No more to GetNext */
*cursor = -1;
} else {
*cursor = idx;
}
return bSuccess;
}
/** Check if the destination network is reachable - is it our virtual network,
* or local or else broadcast.
*
* @param dest_net [in] The BACnet network number of a message's destination.
* Success if it is our virtual network number, or 0 (local for
* the gateway, or 0xFFFF for a broadcast network number.
* @param DNET_list [in] List of our reachable downstream BACnet Network
* numbers. Normally just one valid entry; terminated with a -1 value.
* @return True if matches our virtual network, or is for the local network
* Device (the gateway), or is BACNET_BROADCAST_NETWORK,
* which is an automatic match. Else False if not a reachable network.
*/
bool Routed_Device_Is_Valid_Network(uint16_t dest_net, int *DNET_list)
{
int dnet = DNET_list[0]; /* Get the DNET of our virtual network */
bool bSuccess = false;
/* First, see if it's a BACnet broadcast (automatic pass). */
if (dest_net == BACNET_BROADCAST_NETWORK) {
bSuccess = true;
/* Or see if it's for the main Gateway Device, because
* there's no routing info.
*/
} else if (dest_net == 0) {
bSuccess = true;
/* Or see if matches our virtual DNET */
} else if (dest_net == dnet) {
bSuccess = true;
}
return bSuccess;
}
/* methods to override the normal Device objection functions */
uint32_t Routed_Device_Index_To_Instance(unsigned index)
{
index = index;
return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number;
}
/** See if the requested Object instance matches that for the currently
* indexed Device Object.
* iCurrent_Device_Idx must have been set to point to this Device Object
* before this function is called.
* @param object_id [in] Object ID of the desired Device object.
* If the wildcard value (BACNET_MAX_INSTANCE), always
* matches.
* @return True if Object ID matches the present Device, else False.
*/
bool Routed_Device_Valid_Object_Instance_Number(uint32_t object_id)
{
bool bResult = false;
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (pDev->bacObj.Object_Instance_Number == object_id) {
bResult = true;
}
return bResult;
}
bool Routed_Device_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (object_instance == pDev->bacObj.Object_Instance_Number) {
return characterstring_init_ansi(object_name, pDev->bacObj.Object_Name);
}
return false;
}
/** Manages ReadProperty service for fields which are different for routed
* Devices, or hands off to the default Device RP function for the rest.
* @param rpdata [in] Structure which describes the property to be read.
* @return The length of the apdu encoded, or BACNET_STATUS_ERROR for error or
* BACNET_STATUS_ABORT for abort message.
*/
int Routed_Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_DEVICE, pDev->bacObj.Object_Instance_Number);
break;
case PROP_OBJECT_NAME:
characterstring_init_ansi(&char_string, pDev->bacObj.Object_Name);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string, pDev->Description);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DATABASE_REVISION:
apdu_len =
encode_application_unsigned(&apdu[0], pDev->Database_Revision);
break;
default:
apdu_len = Device_Read_Property_Local(rpdata);
break;
}
return (apdu_len);
}
bool Routed_Device_Write_Property_Local(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);
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_OBJECT_LIST) &&
(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;
}
/* FIXME: len < application_data_len: more data? */
switch (wp_data->object_property) {
case PROP_OBJECT_IDENTIFIER:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_OBJECT_ID,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if ((value.type.Object_Id.type == OBJECT_DEVICE) &&
(Routed_Device_Set_Object_Instance_Number(
value.type.Object_Id.instance))) {
/* FIXME: we could send an I-Am broadcast to let the world
* know */
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_OBJECT_NAME:
status = WPValidateString(&value, MAX_DEV_NAME_LEN, false,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Routed_Device_Set_Object_Name(
characterstring_encoding(&value.type.Character_String),
characterstring_value(&value.type.Character_String),
characterstring_length(&value.type.Character_String));
}
break;
default:
status = Device_Write_Property_Local(wp_data);
break;
}
return status;
}
/* methods to manipulate the data */
/** Return the Object Instance number for the currently active Device Object.
* This is an overload of the important, widely used
* Device_Object_Instance_Number() function.
*
* @return The Instance number of the currently active Device.
*/
uint32_t Routed_Device_Object_Instance_Number(void)
{
return Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number;
}
bool Routed_Device_Set_Object_Instance_Number(uint32_t object_id)
{
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
/* Make the change and update the database revision */
Devices[iCurrent_Device_Idx].bacObj.Object_Instance_Number = object_id;
Routed_Device_Inc_Database_Revision();
} else {
status = false;
}
return status;
}
/** Sets the Object Name for a routed Device (or the gateway).
* Uses local variable iCurrent_Device_Idx to know which Device
* is to be updated.
* @param object_name [in] Character String for the new Object Name.
* @return True if succeed in updating Object Name, else False.
*/
bool Routed_Device_Set_Object_Name(
uint8_t encoding, const char *value, size_t length)
{
bool status = false; /*return value */
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if ((encoding == CHARACTER_UTF8) && (length < MAX_DEV_NAME_LEN)) {
/* Make the change and update the database revision */
memmove(pDev->bacObj.Object_Name, value, length);
pDev->bacObj.Object_Name[length] = 0;
Routed_Device_Inc_Database_Revision();
status = true;
}
return status;
}
bool Routed_Device_Set_Description(const char *name, size_t length)
{
bool status = false; /*return value */
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
if (length < MAX_DEV_DESC_LEN) {
memmove(pDev->Description, name, length);
pDev->Description[length] = 0;
status = true;
}
return status;
}
/*
* Shortcut for incrementing database revision as this is potentially
* the most common operation if changing object names and ids is
* implemented.
*/
void Routed_Device_Inc_Database_Revision(void)
{
DEVICE_OBJECT_DATA *pDev = &Devices[iCurrent_Device_Idx];
pDev->Database_Revision++;
}
/** Check to see if the current Device supports this service.
* Presently checks for RD and DCC and only allows them if the current
* device is the gateway device.
*
* @param service [in] The service being requested.
* @param service_argument [in] An optional argument (eg, service type).
* @param apdu_buff [in,out] The buffer where we will encode a Reject message.
* May be NULL if don't want an encoded response.
* @param invoke_id [in] The invoke_id of the service request.
* @return Length of bytes encoded in apdu_buff[] for a Reject message,
* just 1 if no apdu_buff was supplied and service is not supported,
* else 0 if service is approved for the current device.
*/
int Routed_Device_Service_Approval(BACNET_CONFIRMED_SERVICE service,
int service_argument,
uint8_t *apdu_buff,
uint8_t invoke_id)
{
int len = 0;
switch (service) {
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
/* If not the gateway device, we don't support RD */
if (iCurrent_Device_Idx > 0) {
if (apdu_buff != NULL) {
len = reject_encode_apdu(apdu_buff, invoke_id,
REJECT_REASON_UNRECOGNIZED_SERVICE);
} else {
len = 1; /* Non-zero return */
}
}
break;
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
/* If not the gateway device, we don't support DCC */
if (iCurrent_Device_Idx > 0) {
if (apdu_buff != NULL) {
len = reject_encode_apdu(apdu_buff, invoke_id,
REJECT_REASON_UNRECOGNIZED_SERVICE);
} else {
len = 1; /* Non-zero return */
}
}
break;
default:
/* Everything else is a pass, at this time. */
break;
}
return len;
}
+479
View File
@@ -0,0 +1,479 @@
/**
* @file
* @author Steve Karg
* @date 2014
* @brief Integer Value objects, customize for your use
*
* @section DESCRIPTION
*
* The Integer Value object is an object with a present-value that
* uses an INTEGER data type.
*
* @section LICENSE
*
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
/* me! */
#include "bacnet/basic/object/iv.h"
#ifndef MAX_INTEGER_VALUES
#define MAX_INTEGER_VALUES 1
#endif
struct integer_object {
bool Out_Of_Service : 1;
int32_t Present_Value;
uint16_t Units;
};
struct integer_object Integer_Value[MAX_INTEGER_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Integer_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_UNITS, -1 };
static const int Integer_Value_Properties_Optional[] = { PROP_OUT_OF_SERVICE,
-1 };
static const int Integer_Value_Properties_Proprietary[] = { -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Integer_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Integer_Value_Properties_Required;
}
if (pOptional) {
*pOptional = Integer_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Integer_Value_Properties_Proprietary;
}
return;
}
/**
* Determines if a given Analog Value instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Integer_Value_Valid_Instance(uint32_t object_instance)
{
unsigned int index;
index = Integer_Value_Instance_To_Index(object_instance);
if (index < MAX_INTEGER_VALUES) {
return true;
}
return false;
}
/**
* Determines the number of Analog Value objects
*
* @return Number of Analog Value objects
*/
unsigned Integer_Value_Count(void)
{
return MAX_INTEGER_VALUES;
}
/**
* Determines the object instance-number for a given 0..N index
* of Analog Value objects where N is Integer_Value_Count().
*
* @param index - 0..MAX_INTEGER_VALUES value
*
* @return object instance-number for the given index
*/
uint32_t Integer_Value_Index_To_Instance(unsigned index)
{
uint32_t instance = 1;
instance += index;
return instance;
}
/**
* For a given object instance-number, determines a 0..N index
* of Analog Value objects where N is Integer_Value_Count().
*
* @param object_instance - object-instance number of the object
*
* @return index for the given instance-number, or MAX_INTEGER_VALUES
* if not valid.
*/
unsigned Integer_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_INTEGER_VALUES;
if (object_instance) {
index = object_instance - 1;
if (index > MAX_INTEGER_VALUES) {
index = MAX_INTEGER_VALUES;
}
}
return index;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
int32_t Integer_Value_Present_Value(uint32_t object_instance)
{
int32_t value = 0;
unsigned int index;
index = Integer_Value_Instance_To_Index(object_instance);
if (index < MAX_INTEGER_VALUES) {
value = Integer_Value[index].Present_Value;
}
return value;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - integer value
*
* @return true if values are within range and present-value is set.
*/
bool Integer_Value_Present_Value_Set(
uint32_t object_instance, int32_t value, uint8_t priority)
{
bool status = false;
unsigned int index;
(void)priority;
index = Integer_Value_Instance_To_Index(object_instance);
if (index < MAX_INTEGER_VALUES) {
Integer_Value[index].Present_Value = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring. Note that the object name must be unique
* within this device.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Integer_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
char text_string[32] = "";
unsigned int index;
bool status = false;
index = Integer_Value_Instance_To_Index(object_instance);
if (index < MAX_INTEGER_VALUES) {
sprintf(
text_string, "ANALOG VALUE %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/**
* For a given object instance-number, returns the units property value
*
* @param object_instance - object-instance number of the object
*
* @return units property value
*/
uint16_t Integer_Value_Units(uint32_t instance)
{
unsigned int index;
uint16_t units = UNITS_NO_UNITS;
index = Integer_Value_Instance_To_Index(instance);
if (index < MAX_INTEGER_VALUES) {
units = Integer_Value[index].Units;
}
return units;
}
/**
* For a given object instance-number, sets the units property value
*
* @param object_instance - object-instance number of the object
* @param units - units property value
*
* @return true if the units property value was set
*/
bool Integer_Value_Units_Set(uint32_t instance, uint16_t units)
{
unsigned int index = 0;
bool status = false;
index = Integer_Value_Instance_To_Index(instance);
if (index < MAX_INTEGER_VALUES) {
Integer_Value[index].Units = units;
status = true;
}
return status;
}
/**
* For a given object instance-number, returns the out-of-service
* property value
*
* @param object_instance - object-instance number of the object
*
* @return out-of-service property value
*/
bool Integer_Value_Out_Of_Service(uint32_t instance)
{
unsigned int index = 0;
bool value = false;
index = Integer_Value_Instance_To_Index(instance);
if (index < MAX_INTEGER_VALUES) {
value = Integer_Value[index].Out_Of_Service;
}
return value;
}
/**
* For a given object instance-number, sets the out-of-service property value
*
* @param object_instance - object-instance number of the object
* @param value - boolean out-of-service value
*
* @return true if the out-of-service property value was set
*/
void Integer_Value_Out_Of_Service_Set(uint32_t instance, bool value)
{
unsigned int index = 0;
index = Integer_Value_Instance_To_Index(instance);
if (index < MAX_INTEGER_VALUES) {
Integer_Value[index].Out_Of_Service = value;
}
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Integer_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
uint32_t units = 0;
int32_t integer_value = 0.0;
bool state = false;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_INTEGER_VALUE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Integer_Value_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_INTEGER_VALUE);
break;
case PROP_PRESENT_VALUE:
integer_value =
Integer_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_signed(&apdu[0], integer_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 = Integer_Value_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_OUT_OF_SERVICE:
state = Integer_Value_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_UNITS:
units = Integer_Value_Units(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], units);
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->object_property != PROP_EVENT_TIME_STAMPS) &&
(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;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Integer_Value_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;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_SIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Integer_Value_Present_Value_Set(wp_data->object_instance,
value.type.Signed_Int, wp_data->priority);
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Integer_Value_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_UNITS:
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;
}
/**
* Initializes the Integer Value object data
*/
void Integer_Value_Init(void)
{
unsigned index = 0;
for (index = 0; index < MAX_INTEGER_VALUES; index++) {
Integer_Value[index].Present_Value = 0;
Integer_Value[index].Out_Of_Service = false;
Integer_Value[index].Units = UNITS_NO_UNITS;
}
}
+120
View File
@@ -0,0 +1,120 @@
/**
* @file
* @author Steve Karg
* @date 2014
* @brief Integer Value objects, customize for your use
*
* @section DESCRIPTION
*
* The Integer Value object is an object with a present-value that
* uses an INTEGER data type.
*
* @section LICENSE
*
* 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 IV_H
#define IV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Integer_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Integer_Value_Valid_Instance(
uint32_t object_instance);
unsigned Integer_Value_Count(
void);
uint32_t Integer_Value_Index_To_Instance(
unsigned index);
unsigned Integer_Value_Instance_To_Index(
uint32_t object_instance);
bool Integer_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int Integer_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Integer_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Integer_Value_Present_Value_Set(
uint32_t object_instance,
int32_t value,
uint8_t priority);
int32_t Integer_Value_Present_Value(
uint32_t object_instance);
bool Integer_Value_Change_Of_Value(
uint32_t instance);
void Integer_Value_Change_Of_Value_Clear(
uint32_t instance);
bool Integer_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Integer_Value_COV_Increment(
uint32_t instance);
void Integer_Value_COV_Increment_Set(
uint32_t instance,
float value);
char *Integer_Value_Description(
uint32_t instance);
bool Integer_Value_Description_Set(
uint32_t instance,
char *new_name);
uint16_t Integer_Value_Units(
uint32_t instance);
bool Integer_Value_Units_Set(
uint32_t instance,
uint16_t unit);
bool Integer_Value_Out_Of_Service(
uint32_t instance);
void Integer_Value_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
void Integer_Value_Init(
void);
#ifdef TEST
#include "ctest.h"
void testInteger_Value(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
File diff suppressed because it is too large Load Diff
+79
View File
@@ -0,0 +1,79 @@
/**************************************************************************
*
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
*
* 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 LOADCONTROL_H
#define LOADCONTROL_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Load_Control_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
void Load_Control_State_Machine_Handler(
void);
bool Load_Control_Valid_Instance(
uint32_t object_instance);
unsigned Load_Control_Count(
void);
uint32_t Load_Control_Index_To_Instance(
unsigned index);
unsigned Load_Control_Instance_To_Index(
uint32_t object_instance);
bool Load_Control_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
void Load_Control_Init(
void);
void Load_Control_State_Machine(
int object_index);
int Load_Control_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Load_Control_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef TEST
#include "ctest.h"
void testLoadControl(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
Binary file not shown.
+42
View File
@@ -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_LOAD_CONTROL
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = lc.c ao.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = load_control
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
File diff suppressed because it is too large Load Diff
+181
View File
@@ -0,0 +1,181 @@
/**************************************************************************
*
* Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
*
* 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 LIGHTING_OUTPUT_H
#define LIGHTING_OUTPUT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Lighting_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Lighting_Output_Valid_Instance(
uint32_t object_instance);
unsigned Lighting_Output_Count(
void);
uint32_t Lighting_Output_Index_To_Instance(
unsigned index);
unsigned Lighting_Output_Instance_To_Index(
uint32_t instance);
bool Lighting_Output_Object_Instance_Add(
uint32_t instance);
float Lighting_Output_Present_Value(
uint32_t object_instance);
unsigned Lighting_Output_Present_Value_Priority(
uint32_t object_instance);
bool Lighting_Output_Present_Value_Set(
uint32_t object_instance,
float value,
unsigned priority);
bool Lighting_Output_Present_Value_Relinquish(
uint32_t object_instance,
unsigned priority);
float Lighting_Output_Relinquish_Default(
uint32_t object_instance);
bool Lighting_Output_Relinquish_Default_Set(
uint32_t object_instance,
float value);
bool Lighting_Output_Change_Of_Value(
uint32_t instance);
void Lighting_Output_Change_Of_Value_Clear(
uint32_t instance);
bool Lighting_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Lighting_Output_COV_Increment(
uint32_t instance);
void Lighting_Output_COV_Increment_Set(
uint32_t instance,
float value);
bool Lighting_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Lighting_Output_Name_Set(
uint32_t object_instance,
char *new_name);
char *Lighting_Output_Description(
uint32_t instance);
bool Lighting_Output_Description_Set(
uint32_t instance,
char *new_name);
bool Lighting_Output_Out_Of_Service(
uint32_t instance);
void Lighting_Output_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
bool Lighting_Output_Lighting_Command_Set(
uint32_t object_instance,
BACNET_LIGHTING_COMMAND *value);
bool Lighting_Output_Lighting_Command(
uint32_t object_instance,
BACNET_LIGHTING_COMMAND *value);
BACNET_LIGHTING_IN_PROGRESS Lighting_Output_In_Progress(
uint32_t object_instance);
bool Lighting_Output_In_Progress_Set(
uint32_t object_instance,
BACNET_LIGHTING_IN_PROGRESS in_progress);
float Lighting_Output_Tracking_Value(
uint32_t object_instance);
bool Lighting_Output_Tracking_Value_Set(
uint32_t object_instance,
float value);
bool Lighting_Output_Blink_Warn_Enable(
uint32_t object_instance);
bool Lighting_Output_Blink_Warn_Enable_Set(
uint32_t object_instance,
bool enable);
uint32_t Lighting_Output_Egress_Time(
uint32_t object_instance);
bool Lighting_Output_Egress_Time_Set(
uint32_t object_instance,
uint32_t seconds);
bool Lighting_Output_Egress_Active(
uint32_t object_instance);
uint32_t Lighting_Output_Default_Fade_Time(
uint32_t object_instance);
bool Lighting_Output_Default_Fade_Time_Set(
uint32_t object_instance,
uint32_t milliseconds);
float Lighting_Output_Default_Ramp_Rate(
uint32_t object_instance);
bool Lighting_Output_Default_Ramp_Rate_Set(
uint32_t object_instance,
float percent_per_second);
float Lighting_Output_Default_Step_Increment(
uint32_t object_instance);
bool Lighting_Output_Default_Step_Increment_Set(
uint32_t object_instance,
float step_increment);
unsigned Lighting_Output_Default_Priority(
uint32_t object_instance);
bool Lighting_Output_Default_Priority_Set(
uint32_t object_instance,
unsigned priority);
void Lighting_Output_Timer(
uint16_t milliseconds);
void Lighting_Output_Init(
void);
int Lighting_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Lighting_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef TEST
#include "ctest.h"
void testLightingOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_LIGHTING_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = lo.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = lighting_output
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
+441
View File
@@ -0,0 +1,441 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Life Safety Point Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/lsp.h"
#include "bacnet/basic/services.h"
#include "bacnet/proplist.h"
#ifndef MAX_LIFE_SAFETY_POINTS
#define MAX_LIFE_SAFETY_POINTS 7
#endif
/* Here are our stored levels.*/
static BACNET_LIFE_SAFETY_MODE Life_Safety_Point_Mode[MAX_LIFE_SAFETY_POINTS];
static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_State[MAX_LIFE_SAFETY_POINTS];
static BACNET_SILENCED_STATE
Life_Safety_Point_Silenced_State[MAX_LIFE_SAFETY_POINTS];
static BACNET_LIFE_SAFETY_OPERATION
Life_Safety_Point_Operation[MAX_LIFE_SAFETY_POINTS];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Life_Safety_Point_Out_Of_Service[MAX_LIFE_SAFETY_POINTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Life_Safety_Point_Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_PRESENT_VALUE, PROP_TRACKING_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_RELIABILITY, PROP_MODE,
PROP_ACCEPTED_MODES, PROP_SILENCED, PROP_OPERATION_EXPECTED, -1
};
static const int Life_Safety_Point_Properties_Optional[] = { PROP_DESCRIPTION,
-1 };
static const int Life_Safety_Point_Properties_Proprietary[] = { -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Life_Safety_Point_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Life_Safety_Point_Properties_Required;
}
if (pOptional) {
*pOptional = Life_Safety_Point_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Life_Safety_Point_Properties_Proprietary;
}
return;
}
void Life_Safety_Point_Init(void)
{
static bool initialized = false;
unsigned i;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_LIFE_SAFETY_POINTS; i++) {
Life_Safety_Point_Mode[i] = LIFE_SAFETY_MODE_DEFAULT;
Life_Safety_Point_State[i] = LIFE_SAFETY_STATE_QUIET;
Life_Safety_Point_Silenced_State[i] = SILENCED_STATE_UNSILENCED;
Life_Safety_Point_Operation[i] = LIFE_SAFETY_OP_NONE;
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Life_Safety_Point_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_LIFE_SAFETY_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 Life_Safety_Point_Count(void)
{
return MAX_LIFE_SAFETY_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 Life_Safety_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 Life_Safety_Point_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_LIFE_SAFETY_POINTS;
if (object_instance < MAX_LIFE_SAFETY_POINTS) {
index = object_instance;
}
return index;
}
static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value(
uint32_t object_instance)
{
BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET;
unsigned index = 0;
index = Life_Safety_Point_Instance_To_Index(object_instance);
if (index < MAX_LIFE_SAFETY_POINTS) {
present_value = Life_Safety_Point_State[index];
}
return present_value;
}
/* note: the object name must be unique within this device */
bool Life_Safety_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_LIFE_SAFETY_POINTS) {
sprintf(text_string, "LS POINT %u", object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Life_Safety_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;
BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET;
BACNET_LIFE_SAFETY_MODE mode = LIFE_SAFETY_MODE_DEFAULT;
BACNET_SILENCED_STATE silenced_state = SILENCED_STATE_UNSILENCED;
BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OP_NONE;
unsigned object_index = 0;
bool state = false;
BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_LIFE_SAFETY_POINT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Life_Safety_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_LIFE_SAFETY_POINT);
break;
case PROP_PRESENT_VALUE:
present_value =
Life_Safety_Point_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_TRACKING_VALUE:
/* FIXME: tracking value is a local matter how it is derived */
present_value =
Life_Safety_Point_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], 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);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
object_index =
Life_Safety_Point_Instance_To_Index(rpdata->object_instance);
state = Life_Safety_Point_Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_RELIABILITY:
/* see standard for details about this property */
reliability = RELIABILITY_NO_FAULT_DETECTED;
apdu_len = encode_application_enumerated(&apdu[0], reliability);
break;
case PROP_MODE:
object_index =
Life_Safety_Point_Instance_To_Index(rpdata->object_instance);
mode = Life_Safety_Point_Mode[object_index];
apdu_len = encode_application_enumerated(&apdu[0], mode);
break;
case PROP_ACCEPTED_MODES:
for (mode = MIN_LIFE_SAFETY_MODE; mode < MAX_LIFE_SAFETY_MODE;
mode++) {
len = encode_application_enumerated(&apdu[apdu_len], mode);
apdu_len += len;
}
break;
case PROP_SILENCED:
object_index =
Life_Safety_Point_Instance_To_Index(rpdata->object_instance);
silenced_state = Life_Safety_Point_Silenced_State[object_index];
apdu_len = encode_application_enumerated(&apdu[0], silenced_state);
break;
case PROP_OPERATION_EXPECTED:
object_index =
Life_Safety_Point_Instance_To_Index(rpdata->object_instance);
operation = Life_Safety_Point_Operation[object_index];
apdu_len = encode_application_enumerated(&apdu[0], operation);
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 Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
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->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_MODE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Enumerated <= MAX_LIFE_SAFETY_MODE) {
object_index = Life_Safety_Point_Instance_To_Index(
wp_data->object_instance);
Life_Safety_Point_Mode[object_index] =
value.type.Enumerated;
} else {
status = false;
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) {
object_index = Life_Safety_Point_Instance_To_Index(
wp_data->object_instance);
Life_Safety_Point_Out_Of_Service[object_index] =
value.type.Boolean;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_PRESENT_VALUE:
case PROP_TRACKING_VALUE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_RELIABILITY:
case PROP_ACCEPTED_MODES:
case PROP_SILENCED:
case PROP_OPERATION_EXPECTED:
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 <assert.h>
#include <string.h>
#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 testLifeSafetyPoint(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Life_Safety_Point_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_LIFE_SAFETY_POINT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Life_Safety_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_LIFE_SAFETY_POINT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Life Safety Point", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testLifeSafetyPoint);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_LIFE_SAFETY_POINT */
#endif /* TEST */
+72
View File
@@ -0,0 +1,72 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 LSP_H
#define LSP_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Life_Safety_Point_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Life_Safety_Point_Valid_Instance(
uint32_t object_instance);
unsigned Life_Safety_Point_Count(
void);
uint32_t Life_Safety_Point_Index_To_Instance(
unsigned index);
unsigned Life_Safety_Point_Instance_To_Index(
uint32_t object_instance);
bool Life_Safety_Point_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
void Life_Safety_Point_Init(
void);
int Life_Safety_Point_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Life_Safety_Point_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef TEST
#include "ctest.h"
void testLifeSafetyPoint(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_LIFE_SAFETY_POINT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = lsp.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = life_safety_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
+802
View File
@@ -0,0 +1,802 @@
/**************************************************************************
*
* Copyright (C) 2009 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Multi-state Input Objects */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/ms-input.h"
#include "bacnet/basic/services.h"
/* number of demo objects */
#ifndef MAX_MULTISTATE_INPUTS
#define MAX_MULTISTATE_INPUTS 4
#endif
/* how many states? 1 to 254 states - 0 is not allowed. */
#ifndef MULTISTATE_NUMBER_OF_STATES
#define MULTISTATE_NUMBER_OF_STATES (254)
#endif
/* Here is our Present Value */
static uint8_t Present_Value[MAX_MULTISTATE_INPUTS];
/* Writable out-of-service allows others to manipulate our Present Value */
static bool Out_Of_Service[MAX_MULTISTATE_INPUTS];
static char Object_Name[MAX_MULTISTATE_INPUTS][64];
static char Object_Description[MAX_MULTISTATE_INPUTS][64];
static char State_Text[MAX_MULTISTATE_INPUTS][MULTISTATE_NUMBER_OF_STATES][64];
/* 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_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, -1 };
static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_STATE_TEXT,
-1 };
static const int Properties_Proprietary[] = { -1 };
void Multistate_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 Multistate_Input_Init(void)
{
unsigned i;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_MULTISTATE_INPUTS; i++) {
Present_Value[i] = 1;
sprintf(&Object_Name[i][0], "MULTISTATE INPUT %u", i);
sprintf(&Object_Description[i][0], "MULTISTATE INPUT %u", i);
}
return;
}
/* 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 Multistate_Input_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_MULTISTATE_INPUTS;
if (object_instance < MAX_MULTISTATE_INPUTS) {
index = object_instance;
}
return index;
}
/* 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 Multistate_Input_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Multistate_Input_Count(void)
{
return MAX_MULTISTATE_INPUTS;
}
bool Multistate_Input_Valid_Instance(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
return true;
}
return false;
}
static uint32_t Multistate_Input_Max_States(uint32_t instance)
{
return MULTISTATE_NUMBER_OF_STATES;
}
uint32_t Multistate_Input_Present_Value(uint32_t object_instance)
{
uint32_t value = 1;
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
value = Present_Value[index];
}
return value;
}
bool Multistate_Input_Present_Value_Set(
uint32_t object_instance, uint32_t value)
{
bool status = false;
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) {
Present_Value[index] = (uint8_t)value;
status = true;
}
}
return status;
}
bool Multistate_Input_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
value = Out_Of_Service[index];
}
return value;
}
void Multistate_Input_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
unsigned index = 0;
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
Out_Of_Service[index] = value;
}
return;
}
char *Multistate_Input_Description(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
char *pName = NULL; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
pName = Object_Description[index];
}
return pName;
}
bool Multistate_Input_Description_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
status = true;
if (new_name) {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = 0;
}
}
}
return status;
}
static bool Multistate_Input_Description_Write(uint32_t object_instance,
BACNET_CHARACTER_STRING *char_string,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
unsigned index = 0; /* offset from instance lookup */
size_t length = 0;
uint8_t encoding = 0;
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
length = characterstring_length(char_string);
if (length <= sizeof(Object_Description[index])) {
encoding = characterstring_encoding(char_string);
if (encoding == CHARACTER_UTF8) {
status = characterstring_ansi_copy(Object_Description[index],
sizeof(Object_Description[index]), char_string);
if (!status) {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
}
}
return status;
}
bool Multistate_Input_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
unsigned index = 0; /* offset from instance lookup */
bool status = false;
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
status = characterstring_init_ansi(object_name, Object_Name[index]);
}
return status;
}
/* note: the object name must be unique within this device */
bool Multistate_Input_Name_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
status = true;
/* FIXME: check to see if there is a matching name */
if (new_name) {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = 0;
}
}
}
return status;
}
static bool Multistate_Input_Object_Name_Write(uint32_t object_instance,
BACNET_CHARACTER_STRING *char_string,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
unsigned index = 0; /* offset from instance lookup */
size_t length = 0;
uint8_t encoding = 0;
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_INPUTS) {
length = characterstring_length(char_string);
if (length <= sizeof(Object_Name[index])) {
encoding = characterstring_encoding(char_string);
if (encoding == CHARACTER_UTF8) {
status = characterstring_ansi_copy(Object_Name[index],
sizeof(Object_Name[index]), char_string);
if (!status) {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
}
}
return status;
}
char *Multistate_Input_State_Text(
uint32_t object_instance, uint32_t state_index)
{
unsigned index = 0; /* offset from instance lookup */
char *pName = NULL; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) &&
(state_index <= MULTISTATE_NUMBER_OF_STATES)) {
state_index--;
pName = State_Text[index][state_index];
}
return pName;
}
/* note: the object name must be unique within this device */
bool Multistate_Input_State_Text_Set(
uint32_t object_instance, uint32_t state_index, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) &&
(state_index <= MULTISTATE_NUMBER_OF_STATES)) {
state_index--;
status = true;
if (new_name) {
for (i = 0; i < sizeof(State_Text[index][state_index]); i++) {
State_Text[index][state_index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(State_Text[index][state_index]); i++) {
State_Text[index][state_index][i] = 0;
}
}
}
return status;
;
}
static bool Multistate_Input_State_Text_Write(uint32_t object_instance,
uint32_t state_index,
BACNET_CHARACTER_STRING *char_string,
BACNET_ERROR_CLASS *error_class,
BACNET_ERROR_CODE *error_code)
{
unsigned index = 0; /* offset from instance lookup */
size_t length = 0;
uint8_t encoding = 0;
bool status = false; /* return value */
index = Multistate_Input_Instance_To_Index(object_instance);
if ((index < MAX_MULTISTATE_INPUTS) && (state_index > 0) &&
(state_index <= Multistate_Input_Max_States(object_instance))) {
state_index--;
length = characterstring_length(char_string);
if (length <= sizeof(State_Text[index][state_index])) {
encoding = characterstring_encoding(char_string);
if (encoding == CHARACTER_UTF8) {
status =
characterstring_ansi_copy(State_Text[index][state_index],
sizeof(State_Text[index][state_index]), char_string);
if (!status) {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
}
} else {
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Multistate_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;
uint32_t present_value = 0;
unsigned i = 0;
uint32_t max_states = 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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_MULTI_STATE_INPUT, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
Multistate_Input_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string,
Multistate_Input_Description(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], OBJECT_MULTI_STATE_INPUT);
break;
case PROP_PRESENT_VALUE:
present_value =
Multistate_Input_Present_Value(rpdata->object_instance);
apdu_len = encode_application_unsigned(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
if (Multistate_Input_Out_Of_Service(rpdata->object_instance)) {
bitstring_set_bit(
&bit_string, STATUS_FLAG_OUT_OF_SERVICE, true);
} else {
bitstring_set_bit(
&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
}
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Multistate_Input_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_NUMBER_OF_STATES:
apdu_len = encode_application_unsigned(&apdu[apdu_len],
Multistate_Input_Max_States(rpdata->object_instance));
break;
case PROP_STATE_TEXT:
if (rpdata->array_index == 0) {
/* Array element zero is the number of elements in the array */
apdu_len = encode_application_unsigned(&apdu[0],
Multistate_Input_Max_States(rpdata->object_instance));
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
max_states =
Multistate_Input_Max_States(rpdata->object_instance);
for (i = 1; i <= max_states; i++) {
characterstring_init_ansi(&char_string,
Multistate_Input_State_Text(
rpdata->object_instance, i));
/* FIXME: this might go beyond MAX_APDU length! */
len = encode_application_character_string(
&apdu[apdu_len], &char_string);
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
max_states =
Multistate_Input_Max_States(rpdata->object_instance);
if (rpdata->array_index <= max_states) {
characterstring_init_ansi(&char_string,
Multistate_Input_State_Text(
rpdata->object_instance, rpdata->array_index));
apdu_len = encode_application_character_string(
&apdu[0], &char_string);
} 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_STATE_TEXT) &&
(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 Multistate_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
int element_len = 0;
BACNET_APPLICATION_DATA_VALUE value;
uint32_t max_states = 0;
uint32_t array_index = 0;
int object_type = 0;
uint32_t object_instance = 0;
/* decode the first chunk of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* len < application_data_len: extra data for arrays only */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_STATE_TEXT) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_OBJECT_NAME:
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
/* All the object names in a device must be unique */
if (Device_Valid_Object_Name(&value.type.Character_String,
&object_type, &object_instance)) {
if ((object_type == wp_data->object_type) &&
(object_instance == wp_data->object_instance)) {
/* writing same name to same object */
status = true;
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_DUPLICATE_NAME;
}
} else {
status = Multistate_Input_Object_Name_Write(
wp_data->object_instance, &value.type.Character_String,
&wp_data->error_class, &wp_data->error_code);
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_DESCRIPTION:
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
status = Multistate_Input_Description_Write(
wp_data->object_instance, &value.type.Character_String,
&wp_data->error_class, &wp_data->error_code);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
status = Multistate_Input_Present_Value_Set(
wp_data->object_instance, value.type.Unsigned_Int);
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) {
Multistate_Input_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_STATE_TEXT:
if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) {
if (wp_data->array_index == 0) {
/* Array element zero is the number of
elements in the array. We have a fixed
size array, so we are read-only. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else if (wp_data->array_index == BACNET_ARRAY_ALL) {
max_states =
Multistate_Input_Max_States(wp_data->object_instance);
array_index = 1;
element_len = len;
do {
if (element_len) {
status = Multistate_Input_State_Text_Write(
wp_data->object_instance, array_index,
&value.type.Character_String,
&wp_data->error_class, &wp_data->error_code);
}
max_states--;
array_index++;
if (max_states) {
element_len = bacapp_decode_application_data(
&wp_data->application_data[len],
wp_data->application_data_len - len, &value);
if (element_len < 0) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code =
ERROR_CODE_VALUE_OUT_OF_RANGE;
break;
}
len += element_len;
}
} while (max_states);
} else {
max_states =
Multistate_Input_Max_States(wp_data->object_instance);
if (wp_data->array_index <= max_states) {
status = Multistate_Input_State_Text_Write(
wp_data->object_instance, wp_data->array_index,
&value.type.Character_String, &wp_data->error_class,
&wp_data->error_code);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_NUMBER_OF_STATES:
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 <assert.h>
#include <string.h>
#include "ctest.h"
bool Device_Valid_Object_Name(BACNET_CHARACTER_STRING *object_name,
int *object_type,
uint32_t *object_instance)
{
return true;
}
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 testMultistateInput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Multistate_Input_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_MULTI_STATE_INPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Multistate_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_MULTISTATE_INPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Multi-state Input", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testMultistateInput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
+133
View File
@@ -0,0 +1,133 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 MS_INPUT_H
#define MS_INPUT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Multistate_Input_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Multistate_Input_Valid_Instance(
uint32_t object_instance);
unsigned Multistate_Input_Count(
void);
uint32_t Multistate_Input_Index_To_Instance(
unsigned index);
unsigned Multistate_Input_Instance_To_Index(
uint32_t instance);
int Multistate_Input_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Multistate_Input_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
/* optional API */
bool Multistate_Input_Object_Instance_Add(
uint32_t instance);
bool Multistate_Input_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Multistate_Input_Name_Set(
uint32_t object_instance,
char *new_name);
uint32_t Multistate_Input_Present_Value(
uint32_t object_instance);
bool Multistate_Input_Present_Value_Set(
uint32_t object_instance,
uint32_t value);
bool Multistate_Input_Change_Of_Value(
uint32_t instance);
void Multistate_Input_Change_Of_Value_Clear(
uint32_t instance);
bool Multistate_Input_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Multistate_Input_Out_Of_Service(
uint32_t object_instance);
void Multistate_Input_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
char *Multistate_Input_Description(
uint32_t instance);
bool Multistate_Input_Description_Set(
uint32_t object_instance,
char *text_string);
BACNET_RELIABILITY Multistate_Input_Reliability(
uint32_t object_instance);
bool Multistate_Input_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
bool Multistate_Input_State_Text_Set(
uint32_t object_instance,
uint32_t state_index,
char *new_name);
bool Multistate_Input_Max_States_Set(
uint32_t instance,
uint32_t max_states_requested);
char *Multistate_Input_State_Text(
uint32_t object_instance,
uint32_t state_index);
bool Multistate_Input_Create(
uint32_t object_instance);
bool Multistate_Input_Delete(
uint32_t object_instance);
void Multistate_Input_Cleanup(
void);
void Multistate_Input_Init(
void);
#ifdef TEST
#include "ctest.h"
void testMultistateInput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_MULTISTATE_INPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = ms-input.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = multistate_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
+531
View File
@@ -0,0 +1,531 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Multi-state Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/mso.h"
#include "bacnet/basic/services.h"
#ifndef MAX_MULTISTATE_OUTPUTS
#define MAX_MULTISTATE_OUTPUTS 4
#endif
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value, 0 is not allowed */
#define MULTISTATE_RELINQUISH_DEFAULT 1
/* NULL part of the array */
#define MULTISTATE_NULL (255)
/* how many states? 1 to 254 states, 0 is not allowed */
#define MULTISTATE_NUMBER_OF_STATES (254)
/* Here is our Priority Array.*/
static uint8_t Multistate_Output_Level[MAX_MULTISTATE_OUTPUTS]
[BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_MULTISTATE_OUTPUTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Multistate_Output_Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_EVENT_STATE,
PROP_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, -1
};
static const int Multistate_Output_Properties_Optional[] = { PROP_DESCRIPTION,
-1 };
static const int Multistate_Output_Properties_Proprietary[] = { -1 };
void Multistate_Output_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Multistate_Output_Properties_Required;
}
if (pOptional) {
*pOptional = Multistate_Output_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Multistate_Output_Properties_Proprietary;
}
return;
}
void Multistate_Output_Init(void)
{
unsigned i, j;
static bool initialized = false;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Multistate_Output_Level[i][j] = MULTISTATE_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Multistate_Output_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_MULTISTATE_OUTPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Multistate_Output_Count(void)
{
return MAX_MULTISTATE_OUTPUTS;
}
/* 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 Multistate_Output_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 Multistate_Output_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_MULTISTATE_OUTPUTS;
if (object_instance < MAX_MULTISTATE_OUTPUTS) {
index = object_instance;
}
return index;
}
uint32_t Multistate_Output_Present_Value(uint32_t object_instance)
{
uint32_t value = MULTISTATE_RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Multistate_Output_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Multistate_Output_Level[index][i] != MULTISTATE_NULL) {
value = Multistate_Output_Level[index][i];
break;
}
}
}
return value;
}
/* note: the object name must be unique within this device */
bool Multistate_Output_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_MULTISTATE_OUTPUTS) {
sprintf(text_string, "MULTISTATE OUTPUT %u", object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Multistate_Output_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Multistate_Output_Instance_To_Index(instance);
if (index < MAX_MULTISTATE_OUTPUTS) {
oos_flag = Out_Of_Service[index];
}
return oos_flag;
}
void Multistate_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Multistate_Output_Instance_To_Index(instance);
if (index < MAX_MULTISTATE_OUTPUTS) {
Out_Of_Service[index] = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Multistate_Output_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;
uint32_t present_value = 0;
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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_MULTI_STATE_OUTPUT, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Multistate_Output_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_MULTI_STATE_OUTPUT);
break;
case PROP_PRESENT_VALUE:
present_value =
Multistate_Output_Present_Value(rpdata->object_instance);
apdu_len = encode_application_unsigned(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Multistate_Output_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:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Multistate_Output_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) {
object_index = Multistate_Output_Instance_To_Index(
rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Multistate_Output_Level[object_index][i] ==
MULTISTATE_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
present_value =
Multistate_Output_Level[object_index][i];
len = encode_application_unsigned(
&apdu[apdu_len], present_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index = Multistate_Output_Instance_To_Index(
rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Multistate_Output_Level[object_index]
[rpdata->array_index - 1] ==
MULTISTATE_NULL) {
apdu_len = encode_application_null(&apdu[0]);
} else {
present_value =
Multistate_Output_Level[object_index]
[rpdata->array_index - 1];
apdu_len = encode_application_unsigned(
&apdu[0], present_value);
}
} 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:
present_value = MULTISTATE_RELINQUISH_DEFAULT;
apdu_len = encode_application_unsigned(&apdu[0], present_value);
break;
case PROP_NUMBER_OF_STATES:
apdu_len = encode_application_unsigned(
&apdu[apdu_len], MULTISTATE_NUMBER_OF_STATES);
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_STATE_TEXT) &&
(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 Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
unsigned int priority = 0;
uint32_t level = 0;
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;
}
if ((wp_data->object_property != PROP_STATE_TEXT) &&
(wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
priority = wp_data->priority;
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) &&
(value.type.Unsigned_Int > 0) &&
(value.type.Unsigned_Int <= MULTISTATE_NUMBER_OF_STATES)) {
level = value.type.Unsigned_Int;
object_index = Multistate_Output_Instance_To_Index(
wp_data->object_instance);
priority--;
Multistate_Output_Level[object_index][priority] =
(uint8_t)level;
/* Note: you could set the physical output here if we
are the highest priority.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing
output) */
status = true;
} else if (priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = MULTISTATE_NULL;
object_index = Multistate_Output_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
Multistate_Output_Level[object_index][priority] =
(uint8_t)level;
/* Note: you could set the physical output here to the
next highest priority, or to the relinquish default
if no priorities are set. However, if Out of Service
is TRUE, then don't set the physical output. This
comment may apply to the
main loop (i.e. check out of service before changing
output) */
} else {
status = false;
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) {
Multistate_Output_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_NUMBER_OF_STATES:
case PROP_DESCRIPTION:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
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 <assert.h>
#include <string.h>
#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 testMultistateOutput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Multistate_Output_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_MULTI_STATE_OUTPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Multistate_Output_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_MULTISTATE_OUTPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Multi-state Output", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testMultistateOutput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_INPUT */
#endif /* TEST */
+116
View File
@@ -0,0 +1,116 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* 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 MSO_H
#define MSO_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Multistate_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Multistate_Output_Valid_Instance(
uint32_t object_instance);
unsigned Multistate_Output_Count(
void);
uint32_t Multistate_Output_Index_To_Instance(
unsigned index);
unsigned Multistate_Output_Instance_To_Index(
uint32_t object_instance);
bool Multistate_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
void Multistate_Output_Init(
void);
int Multistate_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Multistate_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Multistate_Output_Change_Of_Value(
uint32_t instance);
void Multistate_Output_Change_Of_Value_Clear(
uint32_t instance);
bool Multistate_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
uint32_t Multistate_Output_Present_Value(
uint32_t instance);
bool Multistate_Output_Present_Value_Set(
uint32_t instance,
unsigned value,
unsigned priority);
bool Multistate_Output_Present_Value_Relinquish(
uint32_t instance,
unsigned priority);
bool Multistate_Output_Out_Of_Service(
uint32_t instance);
void Multistate_Output_Out_Of_Service_Set(
uint32_t instance,
bool value);
char *Multistate_Output_Description(
uint32_t instance);
bool Multistate_Output_Description_Set(
uint32_t object_instance,
char *text_string);
bool Multistate_Output_State_Text_Set(
uint32_t object_instance,
uint32_t state_index,
char *new_name);
bool Multistate_Output_Max_States_Set(
uint32_t instance,
uint32_t max_states_requested);
char *Multistate_Output_State_Text(
uint32_t object_instance,
uint32_t state_index);
#ifdef TEST
#include "ctest.h"
void testMultistateOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_MULTISTATE_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = mso.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = multistate_output
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
+668
View File
@@ -0,0 +1,668 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* 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.
*
*********************************************************************/
/* Multi-state Value Objects */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/msv.h"
#include "bacnet/basic/services.h"
/* number of demo objects */
#ifndef MAX_MULTISTATE_VALUES
#define MAX_MULTISTATE_VALUES 4
#endif
/* how many states? 1 to 254 states - 0 is not allowed. */
#ifndef MULTISTATE_NUMBER_OF_STATES
#define MULTISTATE_NUMBER_OF_STATES (254)
#endif
/* Here is our Present Value */
static uint8_t Present_Value[MAX_MULTISTATE_VALUES];
/* Writable out-of-service allows others to manipulate our Present Value */
static bool Out_Of_Service[MAX_MULTISTATE_VALUES];
/* Change of Value flag */
static bool Change_Of_Value[MAX_MULTISTATE_VALUES];
/* object name storage */
static char Object_Name[MAX_MULTISTATE_VALUES][64];
/* object description storage */
static char Object_Description[MAX_MULTISTATE_VALUES][64];
/* object state text storage */
static char State_Text[MAX_MULTISTATE_VALUES][MULTISTATE_NUMBER_OF_STATES][64];
/* 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_OUT_OF_SERVICE, PROP_NUMBER_OF_STATES, -1 };
static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_STATE_TEXT,
-1 };
static const int Properties_Proprietary[] = { -1 };
void Multistate_Value_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 Multistate_Value_Init(void)
{
unsigned int i;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_MULTISTATE_VALUES; i++) {
Present_Value[i] = 1;
sprintf(&Object_Name[i][0], "MULTISTATE VALUE %u", i);
sprintf(&Object_Description[i][0], "MULTISTATE VALUE %u", i);
}
return;
}
/* 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 Multistate_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_MULTISTATE_VALUES;
if (object_instance < MAX_MULTISTATE_VALUES) {
index = object_instance;
}
return index;
}
/* 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 Multistate_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Multistate_Value_Count(void)
{
return MAX_MULTISTATE_VALUES;
}
bool Multistate_Value_Valid_Instance(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
return true;
}
return false;
}
uint32_t Multistate_Value_Present_Value(uint32_t object_instance)
{
uint32_t value = 1;
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
value = Present_Value[index];
}
return value;
}
bool Multistate_Value_Present_Value_Set(
uint32_t object_instance, uint32_t value)
{
bool status = false;
unsigned index = 0; /* offset from instance lookup */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
if ((value > 0) && (value <= MULTISTATE_NUMBER_OF_STATES)) {
if (Present_Value[index] != (uint8_t)value) {
Change_Of_Value[index] = true;
}
Present_Value[index] = (uint8_t)value;
status = true;
}
}
return status;
}
bool Multistate_Value_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
value = Out_Of_Service[index];
}
return value;
}
void Multistate_Value_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
unsigned index = 0;
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
if (Out_Of_Service[index] != value) {
Change_Of_Value[index] = true;
}
Out_Of_Service[index] = value;
}
return;
}
char *Multistate_Value_Description(uint32_t object_instance)
{
unsigned index = 0; /* offset from instance lookup */
char *pName = NULL; /* return value */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
pName = Object_Description[index];
}
return pName;
}
bool Multistate_Value_Description_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
status = true;
if (new_name) {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Description[index]); i++) {
Object_Description[index][i] = 0;
}
}
}
return status;
}
bool Multistate_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
unsigned index = 0; /* offset from instance lookup */
bool status = false;
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
status = characterstring_init_ansi(object_name, Object_Name[index]);
}
return status;
}
/* note: the object name must be unique within this device */
bool Multistate_Value_Name_Set(uint32_t object_instance, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
status = true;
/* FIXME: check to see if there is a matching name */
if (new_name) {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(Object_Name[index]); i++) {
Object_Name[index][i] = 0;
}
}
}
return status;
}
char *Multistate_Value_State_Text(
uint32_t object_instance, uint32_t state_index)
{
unsigned index = 0; /* offset from instance lookup */
char *pName = NULL; /* return value */
index = Multistate_Value_Instance_To_Index(object_instance);
if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) &&
(state_index <= MULTISTATE_NUMBER_OF_STATES)) {
state_index--;
pName = State_Text[index][state_index];
}
return pName;
}
/* note: the object name must be unique within this device */
bool Multistate_Value_State_Text_Set(
uint32_t object_instance, uint32_t state_index, char *new_name)
{
unsigned index = 0; /* offset from instance lookup */
size_t i = 0; /* loop counter */
bool status = false; /* return value */
index = Multistate_Value_Instance_To_Index(object_instance);
if ((index < MAX_MULTISTATE_VALUES) && (state_index > 0) &&
(state_index <= MULTISTATE_NUMBER_OF_STATES)) {
state_index--;
status = true;
if (new_name) {
for (i = 0; i < sizeof(State_Text[index][state_index]); i++) {
State_Text[index][state_index][i] = new_name[i];
if (new_name[i] == 0) {
break;
}
}
} else {
for (i = 0; i < sizeof(State_Text[index][state_index]); i++) {
State_Text[index][state_index][i] = 0;
}
}
}
return status;
;
}
bool Multistate_Value_Change_Of_Value(uint32_t object_instance)
{
bool status = false;
unsigned index;
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
status = Change_Of_Value[index];
}
return status;
}
void Multistate_Value_Change_Of_Value_Clear(uint32_t object_instance)
{
unsigned index;
index = Multistate_Value_Instance_To_Index(object_instance);
if (index < MAX_MULTISTATE_VALUES) {
Change_Of_Value[index] = false;
}
return;
}
/**
* For a given object instance-number, loads the value_list with the COV data.
*
* @param object_instance - object-instance number of the object
* @param value_list - list of COV data
*
* @return true if the value list is encoded
*/
bool Multistate_Value_Encode_Value_List(
uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list)
{
bool status = false;
if (value_list) {
value_list->propertyIdentifier = PROP_PRESENT_VALUE;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
value_list->value.next = NULL;
value_list->value.type.Enumerated =
Multistate_Value_Present_Value(object_instance);
value_list->priority = BACNET_NO_PRIORITY;
value_list = value_list->next;
}
if (value_list) {
value_list->propertyIdentifier = PROP_STATUS_FLAGS;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING;
value_list->value.next = NULL;
bitstring_init(&value_list->value.type.Bit_String);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false);
if (Multistate_Value_Out_Of_Service(object_instance)) {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, true);
} else {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, false);
}
value_list->priority = BACNET_NO_PRIORITY;
value_list->next = NULL;
status = true;
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Multistate_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint32_t present_value = 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;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_MULTI_STATE_VALUE, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
Multistate_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string,
Multistate_Value_Description(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], OBJECT_MULTI_STATE_VALUE);
break;
case PROP_PRESENT_VALUE:
present_value =
Multistate_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_unsigned(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Multistate_Value_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:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Multistate_Value_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_NUMBER_OF_STATES:
apdu_len = encode_application_unsigned(
&apdu[apdu_len], MULTISTATE_NUMBER_OF_STATES);
break;
case PROP_STATE_TEXT:
if (rpdata->array_index == 0) {
/* Array element zero is the number of elements in the array */
apdu_len = encode_application_unsigned(
&apdu[0], MULTISTATE_NUMBER_OF_STATES);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
for (i = 1; i <= MULTISTATE_NUMBER_OF_STATES; i++) {
characterstring_init_ansi(&char_string,
Multistate_Value_State_Text(
rpdata->object_instance, i));
/* FIXME: this might go beyond MAX_APDU length! */
len = encode_application_character_string(
&apdu[apdu_len], &char_string);
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <= MULTISTATE_NUMBER_OF_STATES) {
characterstring_init_ansi(&char_string,
Multistate_Value_State_Text(
rpdata->object_instance, rpdata->array_index));
apdu_len = encode_application_character_string(
&apdu[0], &char_string);
} 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_STATE_TEXT) &&
(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 Multistate_Value_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;
}
if ((wp_data->object_property != PROP_STATE_TEXT) &&
(wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
status = Multistate_Value_Present_Value_Set(
wp_data->object_instance, value.type.Unsigned_Int);
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) {
Multistate_Value_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_NUMBER_OF_STATES:
case PROP_DESCRIPTION:
case PROP_STATE_TEXT:
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 <assert.h>
#include <string.h>
#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 testMultistateInput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Multistate_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_MULTI_STATE_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Multistate_Value_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_MULTISTATE_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Multi-state Input", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testMultistateInput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif /* TEST */
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* 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 MULTISTATE_VALUE_H
#define MULTISTATE_VALUE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Multistate_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Multistate_Value_Valid_Instance(
uint32_t object_instance);
unsigned Multistate_Value_Count(
void);
uint32_t Multistate_Value_Index_To_Instance(
unsigned index);
unsigned Multistate_Value_Instance_To_Index(
uint32_t instance);
int Multistate_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Multistate_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
/* optional API */
bool Multistate_Value_Object_Instance_Add(
uint32_t instance);
bool Multistate_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Multistate_Value_Name_Set(
uint32_t object_instance,
char *new_name);
uint32_t Multistate_Value_Present_Value(
uint32_t object_instance);
bool Multistate_Value_Present_Value_Set(
uint32_t object_instance,
uint32_t value);
bool Multistate_Value_Change_Of_Value(
uint32_t instance);
void Multistate_Value_Change_Of_Value_Clear(
uint32_t instance);
bool Multistate_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Multistate_Value_Out_Of_Service(
uint32_t object_instance);
void Multistate_Value_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
char *Multistate_Value_Description(
uint32_t instance);
bool Multistate_Value_Description_Set(
uint32_t object_instance,
char *text_string);
bool Multistate_Value_State_Text_Set(
uint32_t object_instance,
uint32_t state_index,
char *new_name);
bool Multistate_Value_Max_States_Set(
uint32_t instance,
uint32_t max_states_requested);
char *Multistate_Value_State_Text(
uint32_t object_instance,
uint32_t state_index);
void Multistate_Value_Init(
void);
#ifdef TEST
#include "ctest.h"
void testMultistateValue(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_MULTISTATE_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = msv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = multistate_value
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
+941
View File
@@ -0,0 +1,941 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* 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.
*
* Additional changes, Copyright (c) 2018 Ed Hague <edward@bac-test.com>
*
* 2018.06.17 - Attempting to write to Object_Name returned
*UNKNOWN_PROPERTY. Now returns WRITE_ACCESS_DENIED
*
*********************************************************************/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/basic/binding/address.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/basic/services.h"
#include "bacnet/config.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/event.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/nc.h"
#ifndef MAX_NOTIFICATION_CLASSES
#define MAX_NOTIFICATION_CLASSES 2
#endif
#if defined(INTRINSIC_REPORTING)
static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Notification_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_NOTIFICATION_CLASS, PROP_PRIORITY,
PROP_ACK_REQUIRED, PROP_RECIPIENT_LIST, -1 };
static const int Notification_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Notification_Properties_Proprietary[] = { -1 };
void Notification_Class_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired)
*pRequired = Notification_Properties_Required;
if (pOptional)
*pOptional = Notification_Properties_Optional;
if (pProprietary)
*pProprietary = Notification_Properties_Proprietary;
return;
}
void Notification_Class_Init(void)
{
uint8_t NotifyIdx = 0;
for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) {
/* init with zeros */
memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO));
/* set the basic parameters */
NC_Info[NotifyIdx].Ack_Required = 0;
NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] =
255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] =
255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] =
255; /* The lowest priority for Normal message. */
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Notification_Class_Valid_Instance(uint32_t object_instance)
{
unsigned int index;
index = Notification_Class_Instance_To_Index(object_instance);
if (index < MAX_NOTIFICATION_CLASSES)
return true;
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Notification_Class_Count(void)
{
return MAX_NOTIFICATION_CLASSES;
}
/* 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 Notification_Class_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 Notification_Class_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_NOTIFICATION_CLASSES;
if (object_instance < MAX_NOTIFICATION_CLASSES)
index = object_instance;
return index;
}
bool Notification_Class_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
unsigned int index;
bool status = false;
index = Notification_Class_Instance_To_Index(object_instance);
if (index < MAX_NOTIFICATION_CLASSES) {
sprintf(text_string, "NOTIFICATION CLASS %lu", (unsigned long)index);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_CHARACTER_STRING char_string;
BACNET_OCTET_STRING octet_string;
BACNET_BIT_STRING bit_string;
uint8_t *apdu = NULL;
uint8_t u8Val;
int idx;
int apdu_len = 0; /* return value */
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
CurrentNotify =
&NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)];
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_NOTIFICATION_CLASS, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Notification_Class_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_NOTIFICATION_CLASS);
break;
case PROP_NOTIFICATION_CLASS:
apdu_len +=
encode_application_unsigned(&apdu[0], rpdata->object_instance);
break;
case PROP_PRIORITY:
if (rpdata->array_index == 0)
apdu_len += encode_application_unsigned(&apdu[0], 3);
else {
if (rpdata->array_index == BACNET_ARRAY_ALL) {
apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL]);
apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[TRANSITION_TO_FAULT]);
apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[TRANSITION_TO_NORMAL]);
} else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) {
apdu_len += encode_application_unsigned(&apdu[apdu_len],
CurrentNotify->Priority[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = -1;
}
}
break;
case PROP_ACK_REQUIRED:
u8Val = CurrentNotify->Ack_Required;
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL,
(u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false);
bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT,
(u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false);
bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL,
(u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false);
/* encode bitstring */
apdu_len +=
encode_application_bitstring(&apdu[apdu_len], &bit_string);
break;
case PROP_RECIPIENT_LIST:
/* encode all entry of Recipient_List */
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_DESTINATION *RecipientEntry;
int i = 0;
/* get pointer of current element for Recipient_List - easier
* for use */
RecipientEntry = &CurrentNotify->Recipient_List[idx];
if (RecipientEntry->Recipient.RecipientType !=
RECIPIENT_TYPE_NOTINITIALIZED) {
/* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday
*/
u8Val = 0x01;
bitstring_init(&bit_string);
for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) {
if (RecipientEntry->ValidDays & u8Val)
bitstring_set_bit(&bit_string, i, true);
else
bitstring_set_bit(&bit_string, i, false);
u8Val <<= 1; /* next day */
}
apdu_len += encode_application_bitstring(
&apdu[apdu_len], &bit_string);
/* From Time */
apdu_len += encode_application_time(
&apdu[apdu_len], &RecipientEntry->FromTime);
/* To Time */
apdu_len += encode_application_time(
&apdu[apdu_len], &RecipientEntry->ToTime);
/*
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
} */
/* CHOICE - device [0] BACnetObjectIdentifier */
if (RecipientEntry->Recipient.RecipientType ==
RECIPIENT_TYPE_DEVICE) {
apdu_len += encode_context_object_id(&apdu[apdu_len], 0,
OBJECT_DEVICE,
RecipientEntry->Recipient._.DeviceIdentifier);
}
/* CHOICE - address [1] BACnetAddress */
else if (RecipientEntry->Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
/* opening tag 1 */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
/* network-number Unsigned16, */
apdu_len += encode_application_unsigned(&apdu[apdu_len],
RecipientEntry->Recipient._.Address.net);
/* mac-address OCTET STRING */
if (RecipientEntry->Recipient._.Address.net) {
octetstring_init(&octet_string,
RecipientEntry->Recipient._.Address.adr,
RecipientEntry->Recipient._.Address.len);
} else {
octetstring_init(&octet_string,
RecipientEntry->Recipient._.Address.mac,
RecipientEntry->Recipient._.Address.mac_len);
}
apdu_len += encode_application_octet_string(
&apdu[apdu_len], &octet_string);
/* closing tag 1 */
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
} else {
;
} /* shouldn't happen */
/* Process Identifier - Unsigned32 */
apdu_len += encode_application_unsigned(
&apdu[apdu_len], RecipientEntry->ProcessIdentifier);
/* Issue Confirmed Notifications - boolean */
apdu_len += encode_application_boolean(
&apdu[apdu_len], RecipientEntry->ConfirmedNotify);
/* Transitions - BACnet Event Transition Bits [bitstring] */
u8Val = RecipientEntry->Transitions;
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL,
(u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true
: false);
bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT,
(u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false);
bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL,
(u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false);
apdu_len += encode_application_bitstring(
&apdu[apdu_len], &bit_string);
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY) &&
(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;
}
bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
NOTIFICATION_CLASS_INFO TmpNotify;
BACNET_APPLICATION_DATA_VALUE value;
uint8_t TmpPriority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of
Unsigned */
bool status = false;
int iOffset;
uint8_t idx;
int len = 0;
CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(
wp_data->object_instance)];
/* decode some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRIORITY:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (wp_data->array_index == 0) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
status = false;
} else if (wp_data->array_index == BACNET_ARRAY_ALL) {
iOffset = 0;
for (idx = 0; idx < MAX_BACNET_EVENT_TRANSITION; idx++) {
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag !=
BACNET_APPLICATION_TAG_UNSIGNED_INT)) {
/* Bad decode, wrong tag or following required
* parameter missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
status = false;
break;
}
if (value.type.Unsigned_Int > 255) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
status = false;
break;
}
TmpPriority[idx] = (uint8_t)value.type.Unsigned_Int;
iOffset += len;
}
if (status == true) {
for (idx = 0; idx < MAX_BACNET_EVENT_TRANSITION; idx++)
CurrentNotify->Priority[idx] = TmpPriority[idx];
}
} else if (wp_data->array_index <= 3) {
if (value.type.Unsigned_Int > 255) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
status = false;
} else
CurrentNotify->Priority[wp_data->array_index - 1] =
value.type.Unsigned_Int;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
status = false;
}
}
break;
case PROP_ACK_REQUIRED:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Bit_String.bits_used == 3) {
CurrentNotify->Ack_Required =
value.type.Bit_String.value[0];
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_RECIPIENT_LIST:
memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO));
idx = 0;
iOffset = 0;
/* decode all packed */
while (iOffset < wp_data->application_data_len) {
/* Decode Valid Days */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK)
/* store value */
TmpNotify.Recipient_List[idx].ValidDays =
value.type.Bit_String.value[0];
else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_OTHER;
return false;
}
iOffset += len;
/* Decode From Time */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].FromTime = value.type.Time;
iOffset += len;
/* Decode To Time */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ToTime = value.type.Time;
iOffset += len;
/* context tag [0] - Device */
if (decode_is_context_tag(
&wp_data->application_data[iOffset], 0)) {
TmpNotify.Recipient_List[idx].Recipient.RecipientType =
RECIPIENT_TYPE_DEVICE;
/* Decode Network Number */
len = bacapp_decode_context_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value,
PROP_RECIPIENT_LIST);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier =
value.type.Object_Id.instance;
iOffset += len;
}
/* opening tag [1] - Recipient */
else if (decode_is_opening_tag_number(
&wp_data->application_data[iOffset], 1)) {
iOffset++;
TmpNotify.Recipient_List[idx].Recipient.RecipientType =
RECIPIENT_TYPE_ADDRESS;
/* Decode Network Number */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].Recipient._.Address.net =
value.type.Unsigned_Int;
iOffset += len;
/* Decode Address */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
if (TmpNotify.Recipient_List[idx].Recipient._.Address.net ==
0) {
memcpy(TmpNotify.Recipient_List[idx]
.Recipient._.Address.mac,
value.type.Octet_String.value,
value.type.Octet_String.length);
TmpNotify.Recipient_List[idx]
.Recipient._.Address.mac_len =
value.type.Octet_String.length;
} else {
memcpy(TmpNotify.Recipient_List[idx]
.Recipient._.Address.adr,
value.type.Octet_String.value,
value.type.Octet_String.length);
TmpNotify.Recipient_List[idx].Recipient._.Address.len =
value.type.Octet_String.length;
}
iOffset += len;
/* closing tag [1] - Recipient */
if (decode_is_closing_tag_number(
&wp_data->application_data[iOffset], 1))
iOffset++;
else {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
} else {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* Process Identifier */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ProcessIdentifier =
value.type.Unsigned_Int;
iOffset += len;
/* Issue Confirmed Notifications */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ConfirmedNotify =
value.type.Boolean;
iOffset += len;
/* Transitions */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
if (value.type.Bit_String.bits_used ==
MAX_BACNET_EVENT_TRANSITION)
/* store value */
TmpNotify.Recipient_List[idx].Transitions =
value.type.Bit_String.value[0];
else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_OTHER;
return false;
}
iOffset += len;
/* Increasing element of list */
if (++idx >= NC_MAX_RECIPIENTS) {
wp_data->error_class = ERROR_CLASS_RESOURCES;
wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY;
return false;
}
}
/* Decoded all recipient list */
/* copy elements from temporary object */
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_ADDRESS src = { 0 };
unsigned max_apdu = 0;
int32_t DeviceID;
CurrentNotify->Recipient_List[idx] =
TmpNotify.Recipient_List[idx];
if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* copy Device_ID */
DeviceID = CurrentNotify->Recipient_List[idx]
.Recipient._.DeviceIdentifier;
address_bind_request(DeviceID, &max_apdu, &src);
} else if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
/* copy Address */
/* src =
* CurrentNotify->Recipient_List[idx].Recipient._.Address;
*/
/* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu,
* &src); */
}
}
status = true;
case PROP_OBJECT_NAME:
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;
}
void Notification_Class_Get_Priorities(
uint32_t Object_Instance, uint32_t *pPriorityArray)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
uint32_t object_index;
int i;
object_index = Notification_Class_Instance_To_Index(Object_Instance);
if (object_index < MAX_NOTIFICATION_CLASSES)
CurrentNotify = &NC_Info[object_index];
else {
for (i = 0; i < 3; i++)
pPriorityArray[i] = 255;
return; /* unknown object */
}
for (i = 0; i < 3; i++)
pPriorityArray[i] = CurrentNotify->Priority[i];
}
static bool IsRecipientActive(
BACNET_DESTINATION *pBacDest, uint8_t EventToState)
{
BACNET_DATE_TIME DateTime;
/* valid Transitions */
switch (EventToState) {
case EVENT_STATE_OFFNORMAL:
case EVENT_STATE_HIGH_LIMIT:
case EVENT_STATE_LOW_LIMIT:
if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED))
return false;
break;
case EVENT_STATE_FAULT:
if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED))
return false;
break;
case EVENT_STATE_NORMAL:
if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED))
return false;
break;
default:
return false; /* shouldn't happen */
}
/* get actual date and time */
Device_getCurrentDateTime(&DateTime);
/* valid Days */
if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays))
return false;
/* valid FromTime */
if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0)
return false;
/* valid ToTime */
if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0)
return false;
return true;
}
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA *event_data)
{
/* Fill the parameters common for all types of events. */
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_DESTINATION *pBacDest;
uint32_t notify_index;
uint8_t index;
notify_index =
Notification_Class_Instance_To_Index(event_data->notificationClass);
if (notify_index < MAX_NOTIFICATION_CLASSES)
CurrentNotify = &NC_Info[notify_index];
else
return;
/* Initiating Device Identifier */
event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE;
event_data->initiatingObjectIdentifier.instance =
Device_Object_Instance_Number();
/* Priority and AckRequired */
switch (event_data->toState) {
case EVENT_STATE_NORMAL:
event_data->priority =
CurrentNotify->Priority[TRANSITION_TO_NORMAL];
event_data->ackRequired =
(CurrentNotify->Ack_Required & TRANSITION_TO_NORMAL_MASKED)
? true
: false;
break;
case EVENT_STATE_FAULT:
event_data->priority = CurrentNotify->Priority[TRANSITION_TO_FAULT];
event_data->ackRequired =
(CurrentNotify->Ack_Required & TRANSITION_TO_FAULT_MASKED)
? true
: false;
break;
case EVENT_STATE_OFFNORMAL:
case EVENT_STATE_HIGH_LIMIT:
case EVENT_STATE_LOW_LIMIT:
event_data->priority =
CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL];
event_data->ackRequired =
(CurrentNotify->Ack_Required & TRANSITION_TO_OFFNORMAL_MASKED)
? true
: false;
break;
default: /* shouldn't happen */
break;
}
/* send notifications for active recipients */
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) {
/* check if recipient is defined */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED)
break; /* recipient doesn't defined - end of list */
if (IsRecipientActive(pBacDest, event_data->toState) == true) {
BACNET_ADDRESS dest;
uint32_t device_id;
unsigned max_apdu;
/* Process Identifier */
event_data->processIdentifier = pBacDest->ProcessIdentifier;
/* send notification */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* send notification to the specified device */
device_id = pBacDest->Recipient._.DeviceIdentifier;
if (pBacDest->ConfirmedNotify == true)
Send_CEvent_Notify(device_id, event_data);
else if (address_get_by_device(device_id, &max_apdu, &dest))
Send_UEvent_Notify(
Handler_Transmit_Buffer, event_data, &dest);
} else if (pBacDest->Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
/* send notification to the address indicated */
if (pBacDest->ConfirmedNotify == true) {
if (address_get_device_id(&dest, &device_id))
Send_CEvent_Notify(device_id, event_data);
} else {
dest = pBacDest->Recipient._.Address;
Send_UEvent_Notify(
Handler_Transmit_Buffer, event_data, &dest);
}
}
}
}
}
/* This function tries to find the addresses of the defined devices. */
/* It should be called periodically (example once per minute). */
void Notification_Class_find_recipient(void)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_DESTINATION *pBacDest;
BACNET_ADDRESS src = { 0 };
unsigned max_apdu = 0;
uint32_t notify_index;
uint32_t DeviceID;
uint8_t idx;
for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES;
notify_index++) {
/* pointer to current notification */
CurrentNotify =
&NC_Info[Notification_Class_Instance_To_Index(notify_index)];
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) {
if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType ==
RECIPIENT_TYPE_DEVICE) {
/* Device ID */
DeviceID = CurrentNotify->Recipient_List[idx]
.Recipient._.DeviceIdentifier;
/* Send who_ is request only when address of device is unknown.
*/
if (!address_bind_request(DeviceID, &max_apdu, &src))
Send_WhoIs(DeviceID, DeviceID);
} else if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
}
}
}
}
#endif /* defined(INTRINSIC_REPORTING) */
+141
View File
@@ -0,0 +1,141 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* 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 NC_H
#define NC_H
#include "bacnet/event.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define NC_RESCAN_RECIPIENTS_SECS 60
/* max "length" of recipient_list */
#define NC_MAX_RECIPIENTS 10
/* Recipient types */
typedef enum {
RECIPIENT_TYPE_NOTINITIALIZED = 0,
RECIPIENT_TYPE_DEVICE = 1,
RECIPIENT_TYPE_ADDRESS = 2
} NC_RECIPIENT_TYPE;
#if defined(INTRINSIC_REPORTING)
/* BACnetRecipient structure */
/*
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
}
*/
typedef struct BACnet_Recipient {
uint8_t RecipientType; /* Type of Recipient */
union {
uint32_t DeviceIdentifier;
BACNET_ADDRESS Address;
} _;
} BACNET_RECIPIENT;
/* BACnetDestination structure */
typedef struct BACnet_Destination {
uint8_t ValidDays;
BACNET_TIME FromTime;
BACNET_TIME ToTime;
BACNET_RECIPIENT Recipient;
uint32_t ProcessIdentifier;
uint8_t Transitions;
bool ConfirmedNotify;
} BACNET_DESTINATION;
/* Structure containing configuration for a Notification Class */
typedef struct Notification_Class_info {
uint8_t Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */
uint8_t Ack_Required; /* BACnetEventTransitionBits */
BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */
} NOTIFICATION_CLASS_INFO;
/* Indicates whether the transaction has been confirmed */
typedef struct Acked_info {
bool bIsAcked; /* true when transitions is acked */
BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */
} ACKED_INFO;
/* Information needed to send AckNotification */
typedef struct Ack_Notification {
bool bSendAckNotify; /* true if need to send AckNotification */
uint8_t EventState;
} ACK_NOTIFICATION;
void Notification_Class_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
void Notification_Class_Init(
void);
bool Notification_Class_Valid_Instance(
uint32_t object_instance);
unsigned Notification_Class_Count(
void);
uint32_t Notification_Class_Index_To_Instance(
unsigned index);
unsigned Notification_Class_Instance_To_Index(
uint32_t object_instance);
bool Notification_Class_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int Notification_Class_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Notification_Class_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
void Notification_Class_Get_Priorities(
uint32_t Object_Instance,
uint32_t * pPriorityArray);
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA * event_data);
void Notification_Class_find_recipient(
void);
#endif /* defined(INTRINSIC_REPORTING) */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* NC_H */
File diff suppressed because it is too large Load Diff
+312
View File
@@ -0,0 +1,312 @@
/**
* @file
* @author Steve Karg
* @date 2016
* @brief Network port objects, customize for your use
*
* @section DESCRIPTION
*
* The Network Port object provides access to the configuration
* and properties of network ports of a device.
*
* @section LICENSE
*
* 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 NETPORT_H
#define NETPORT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/apdu.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Network_Port_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
void Network_Port_Property_List(
uint32_t object_instance,
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Network_Port_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Network_Port_Name_Set(
uint32_t object_instance,
char *new_name);
char *Network_Port_Description(
uint32_t instance);
bool Network_Port_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Network_Port_Reliability(
uint32_t object_instance);
bool Network_Port_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
bool Network_Port_Out_Of_Service(
uint32_t instance);
bool Network_Port_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
uint8_t Network_Port_Type(
uint32_t object_instance);
bool Network_Port_Type_Set(
uint32_t object_instance,
uint8_t value);
uint16_t Network_Port_Network_Number(
uint32_t object_instance);
bool Network_Port_Network_Number_Set(
uint32_t object_instance,
uint16_t value);
BACNET_PORT_QUALITY Network_Port_Quality(
uint32_t object_instance);
bool Network_Port_Quality_Set(
uint32_t object_instance,
BACNET_PORT_QUALITY value);
bool Network_Port_MAC_Address(
uint32_t object_instance,
BACNET_OCTET_STRING *mac_address);
bool Network_Port_MAC_Address_Set(
uint32_t object_instance,
uint8_t *mac_src,
uint8_t mac_len);
uint16_t Network_Port_APDU_Length(
uint32_t object_instance);
bool Network_Port_APDU_Length_Set(
uint32_t object_instance,
uint16_t value);
uint8_t Network_Port_MSTP_Max_Master(
uint32_t object_instance);
bool Network_Port_MSTP_Max_Master_Set(
uint32_t object_instance,
uint8_t value);
uint8_t Network_Port_MSTP_Max_Info_Frames(
uint32_t object_instance);
bool Network_Port_MSTP_Max_Info_Frames_Set(
uint32_t object_instance,
uint8_t value);
float Network_Port_Link_Speed(
uint32_t object_instance);
bool Network_Port_Link_Speed_Set(
uint32_t object_instance,
float value);
bool Network_Port_IP_Address(
uint32_t object_instance,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IP_Address_Get(
uint32_t object_instance,
uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d);
bool Network_Port_IP_Address_Set(
uint32_t object_instance,
uint8_t a, uint8_t b, uint8_t c, uint8_t d);
uint8_t Network_Port_IP_Subnet_Prefix(
uint32_t object_instance);
bool Network_Port_IP_Subnet_Prefix_Set(
uint32_t object_instance,
uint8_t value);
bool Network_Port_IP_Subnet(
uint32_t object_instance,
BACNET_OCTET_STRING *subnet_mask);
bool Network_Port_IP_Subnet_Get(
uint32_t object_instance,
uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d);
bool Network_Port_IP_Subnet_Set(
uint32_t object_instance,
uint8_t a, uint8_t b, uint8_t c, uint8_t d);
bool Network_Port_IP_Gateway(
uint32_t object_instance,
BACNET_OCTET_STRING *subnet_mask);
bool Network_Port_IP_Gateway_Get(
uint32_t object_instance,
uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d);
bool Network_Port_IP_Gateway_Set(
uint32_t object_instance,
uint8_t a, uint8_t b, uint8_t c, uint8_t d);
bool Network_Port_IP_DNS_Server(
uint32_t object_instance,
unsigned index,
BACNET_OCTET_STRING *subnet_mask);
bool Network_Port_IP_DNS_Server_Get(
uint32_t object_instance,
unsigned index,
uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d);
bool Network_Port_IP_DNS_Server_Set(
uint32_t object_instance,
unsigned index,
uint8_t a, uint8_t b, uint8_t c, uint8_t d);
uint16_t Network_Port_BIP_Port(
uint32_t object_instance);
bool Network_Port_BIP_Port_Set(
uint32_t object_instance,
uint16_t value);
BACNET_IP_MODE Network_Port_BIP_Mode(
uint32_t object_instance);
bool Network_Port_BIP_Mode_Set(
uint32_t object_instance,
BACNET_IP_MODE value);
bool Network_Port_BBMD_Accept_FD_Registrations(
uint32_t object_instance);
bool Network_Port_BBMD_Accept_FD_Registrations_Set(
uint32_t object_instance,
bool value);
BACNET_IP_MODE Network_Port_BIP6_Mode(
uint32_t object_instance);
bool Network_Port_BIP6_Mode_Set(
uint32_t object_instance,
BACNET_IP_MODE value);
bool Network_Port_IPv6_Address(
uint32_t object_instance,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IPv6_Address_Set(
uint32_t object_instance,
uint8_t *ip_address);
bool Network_Port_IPv6_Multicast_Address(
uint32_t object_instance,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IPv6_Multicast_Address_Set(
uint32_t object_instance,
uint8_t *ip_address);
uint8_t Network_Port_IPv6_Subnet_Prefix(
uint32_t object_instance);
bool Network_Port_IPv6_Subnet_Prefix_Set(
uint32_t object_instance,
uint8_t value);
bool Network_Port_IPv6_Gateway(
uint32_t object_instance,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IPv6_Gateway_Set(
uint32_t object_instance,
uint8_t *ip_address);
bool Network_Port_IPv6_DNS_Server(
uint32_t object_instance,
unsigned dns_index,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IPv6_DNS_Server_Set(
uint32_t object_instance,
unsigned dns_index,
uint8_t *ip_address);
bool Network_Port_IPv6_DHCP_Server(
uint32_t object_instance,
BACNET_OCTET_STRING *ip_address);
bool Network_Port_IPv6_DHCP_Server_Set(
uint32_t object_instance,
uint8_t *ip_address);
bool Network_Port_IPv6_Zone_Index(
uint32_t object_instance,
BACNET_CHARACTER_STRING *zone_index);
bool Network_Port_IPv6_Gateway_Zone_Index_Set(
uint32_t object_instance,
char *zone_index);
uint16_t Network_Port_BIP6_Port(
uint32_t object_instance);
bool Network_Port_BIP6_Port_Set(
uint32_t object_instance,
uint16_t value);
bool Network_Port_Changes_Pending(
uint32_t instance);
bool Network_Port_Changes_Pending_Set(
uint32_t instance,
bool flag);
bool Network_Port_Valid_Instance(
uint32_t object_instance);
unsigned Network_Port_Count(
void);
uint32_t Network_Port_Index_To_Instance(
unsigned find_index);
unsigned Network_Port_Instance_To_Index(
uint32_t object_instance);
bool Network_Port_Object_Instance_Number_Set(
unsigned index,
uint32_t object_instance);
int Network_Port_Read_Range_BDT(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
int Network_Port_Read_Range_FDT(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
bool Network_Port_Read_Range(
BACNET_READ_RANGE_DATA * pRequest,
RR_PROP_INFO * pInfo);
bool Network_Port_Create(
uint32_t object_instance);
bool Network_Port_Delete(
uint32_t object_instance);
void Network_Port_Cleanup(
void);
void Network_Port_Init(
void);
/* handling for read property service */
int Network_Port_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
/* handling for write property service */
bool Network_Port_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -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 -DBACNET_UNIT_TEST -DTEST_NETWORK_PORT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = netport.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/proplist.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = network_port
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
+199
View File
@@ -0,0 +1,199 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2008 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "bacnet/basic/sys/keylist.h"
#include "bacnet/basic/object/objects.h"
/** @file objects.c Manage Device Objects. */
/* list of devices */
static OS_Keylist Device_List = NULL;
void objects_init(void)
{
if (!Device_List) {
Device_List = Keylist_Create();
}
}
int objects_device_count(void)
{
objects_init();
return Keylist_Count(Device_List);
}
OBJECT_DEVICE_T *objects_device_data(int index)
{
objects_init();
return Keylist_Data_Index(Device_List, index);
}
OBJECT_DEVICE_T *objects_device_by_instance(uint32_t device_instance)
{
objects_init();
return Keylist_Data(Device_List, device_instance);
}
OBJECT_DEVICE_T *objects_device_new(uint32_t device_instance)
{
OBJECT_DEVICE_T *pDevice = NULL;
KEY key = device_instance;
if (Device_List) {
/* does this device already exist? */
pDevice = Keylist_Data(Device_List, key);
if (pDevice) {
memset(pDevice, 0, sizeof(OBJECT_DEVICE_T));
} else {
pDevice = calloc(1, sizeof(OBJECT_DEVICE_T));
if (pDevice) {
pDevice->Object_Identifier.type = OBJECT_DEVICE;
pDevice->Object_Identifier.instance = device_instance;
pDevice->Object_Type = OBJECT_DEVICE;
pDevice->Object_List = Keylist_Create();
Keylist_Data_Add(Device_List, key, pDevice);
} else {
fprintf(stderr,
"Objects: Unable to allocate device %lu buffer\n",
(unsigned long)device_instance);
}
}
}
return pDevice;
}
OBJECT_DEVICE_T *objects_device_delete(int index)
{
OBJECT_DEVICE_T *pDevice = NULL;
BACNET_OBJECT_ID *pObject;
if (Device_List) {
pDevice = Keylist_Data_Delete_By_Index(Device_List, index);
if (pDevice) {
fprintf(stderr, "Objects: removing device %lu",
(unsigned long)pDevice->Object_Identifier.instance);
if (pDevice->Object_List) {
do {
pObject =
Keylist_Data_Delete_By_Index(pDevice->Object_List, 0);
/* free any dynamic memory used */
if (pObject) {
free(pObject);
}
} while (pObject);
Keylist_Delete(pDevice->Object_List);
}
free(pDevice);
}
}
return pDevice;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
/* test the object creation and deletion */
void testBACnetObjectsCompare(
Test *pTest, OBJECT_DEVICE_T *pDevice, uint32_t device_id)
{
ct_test(pTest, pDevice != NULL);
if (pDevice) {
ct_test(pTest, pDevice->Object_List != NULL);
ct_test(pTest, pDevice->Object_Identifier.instance == device_id);
ct_test(pTest, pDevice->Object_Identifier.type == OBJECT_DEVICE);
ct_test(pTest, pDevice->Object_Type == OBJECT_DEVICE);
}
}
void testBACnetObjects(Test *pTest)
{
uint32_t device_id = 0;
unsigned test_point = 0;
const unsigned max_test_points = 20;
OBJECT_DEVICE_T *pDevice;
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_new(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
pDevice = objects_device_by_instance(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
}
ct_test(pTest, max_test_points == objects_device_count());
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_by_instance(device_id);
testBACnetObjectsCompare(pTest, pDevice, device_id);
}
for (test_point = 0; test_point < max_test_points; test_point++) {
device_id = test_point * (BACNET_MAX_INSTANCE / max_test_points);
pDevice = objects_device_data(test_point);
testBACnetObjectsCompare(
pTest, pDevice, Keylist_Key(Device_List, test_point));
}
for (test_point = 0; test_point < max_test_points; test_point++) {
pDevice = objects_device_delete(0);
}
}
#ifdef TEST_OBJECT_LIST
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Objects", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetObjects);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
#endif
+78
View File
@@ -0,0 +1,78 @@
/**************************************************************************
*
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
*
* 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 OBJECTS_H
#define OBJECTS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacstr.h"
#include "bacnet/bacenum.h"
typedef union BACnetScale_t {
float Float;
int32_t Integer;
} BACNET_SCALE_T;
/* structures to hold the data gotten by ReadProperty from the device */
typedef struct object_accumulator_t {
BACNET_OBJECT_ID Object_Identifier;
BACNET_CHARACTER_STRING Object_Name;
BACNET_OBJECT_TYPE Object_Type;
uint32_t Present_Value;
BACNET_STATUS_FLAGS Status_Flags;
BACNET_EVENT_STATE Event_State;
bool Out_Of_Service;
BACNET_SCALE_T Scale;
BACNET_ENGINEERING_UNITS Units;
uint32_t Max_Pres_Value;
} OBJECT_ACCUMULATOR_T;
typedef struct object_device_t {
BACNET_OBJECT_ID Object_Identifier;
BACNET_CHARACTER_STRING Object_Name;
BACNET_OBJECT_TYPE Object_Type;
BACNET_DEVICE_STATUS System_Status;
BACNET_CHARACTER_STRING Vendor_Name;
uint16_t Vendor_Identifier;
BACNET_CHARACTER_STRING Model_Name;
BACNET_CHARACTER_STRING Firmware_Revision;
BACNET_CHARACTER_STRING Application_Software_Version;
BACNET_CHARACTER_STRING Location;
BACNET_CHARACTER_STRING Description;
uint8_t Protocol_Version;
uint8_t Protocol_Revision;
BACNET_BIT_STRING Protocol_Services_Supported;
BACNET_BIT_STRING Protocol_Object_Types_Supported;
OS_Keylist Object_List;
uint32_t Max_APDU_Length_Accepted;
BACNET_SEGMENTATION Segmentation_Supported;
uint32_t APDU_Timeout;
uint8_t Number_Of_APDU_Retries;
uint32_t Database_Revision;
} OBJECT_DEVICE_T;
#endif
+426
View File
@@ -0,0 +1,426 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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.
*
*********************************************************************/
/* OctetString Value Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/osv.h"
#ifndef MAX_OCTETSTRING_VALUES
#define MAX_OCTETSTRING_VALUES 4
#endif
OCTETSTRING_VALUE_DESCR AV_Descr[MAX_OCTETSTRING_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int OctetString_Value_Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, -1
};
static const int OctetString_Value_Properties_Optional[] = { PROP_EVENT_STATE,
PROP_OUT_OF_SERVICE, PROP_DESCRIPTION, -1 };
static const int OctetString_Value_Properties_Proprietary[] = { -1 };
void OctetString_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = OctetString_Value_Properties_Required;
}
if (pOptional) {
*pOptional = OctetString_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = OctetString_Value_Properties_Proprietary;
}
return;
}
void OctetString_Value_Init(void)
{
unsigned i;
for (i = 0; i < MAX_OCTETSTRING_VALUES; i++) {
memset(&AV_Descr[i], 0x00, sizeof(OCTETSTRING_VALUE_DESCR));
octetstring_init(&AV_Descr[i].Present_Value, NULL, 0);
}
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool OctetString_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_OCTETSTRING_VALUES) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned OctetString_Value_Count(void)
{
return MAX_OCTETSTRING_VALUES;
}
/* 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 OctetString_Value_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 OctetString_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_OCTETSTRING_VALUES;
if (object_instance < MAX_OCTETSTRING_VALUES) {
index = object_instance;
}
return index;
}
/**
* For a given object instance-number, sets the present-value at a given
* priority 1..16.
*
* @param object_instance - object-instance number of the object
* @param value - octetstring value
* @param priority - priority 1..16
*
* @return true if values are within range and present-value is set.
*/
bool OctetString_Value_Present_Value_Set(
uint32_t object_instance, BACNET_OCTET_STRING *value, uint8_t priority)
{
unsigned index = 0;
bool status = false;
index = OctetString_Value_Instance_To_Index(object_instance);
if (index < MAX_OCTETSTRING_VALUES) {
octetstring_copy(&AV_Descr[index].Present_Value, value);
status = true;
}
return status;
}
BACNET_OCTET_STRING *OctetString_Value_Present_Value(uint32_t object_instance)
{
BACNET_OCTET_STRING *value = NULL;
unsigned index = 0;
index = OctetString_Value_Instance_To_Index(object_instance);
if (index < MAX_OCTETSTRING_VALUES) {
value = &AV_Descr[index].Present_Value;
}
return value;
}
/* note: the object name must be unique within this device */
bool OctetString_Value_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_OCTETSTRING_VALUES) {
sprintf(text_string, "OCTETSTRING VALUE %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 OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
BACNET_OCTET_STRING *real_value = NULL;
unsigned object_index = 0;
bool state = false;
uint8_t *apdu = NULL;
OCTETSTRING_VALUE_DESCR *CurrentAV;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = OctetString_Value_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_OCTETSTRING_VALUES) {
CurrentAV = &AV_Descr[object_index];
} else {
return BACNET_STATUS_ERROR;
}
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_OCTETSTRING_VALUE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
OctetString_Value_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_OCTETSTRING_VALUE);
break;
case PROP_PRESENT_VALUE:
real_value =
OctetString_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_octet_string(&apdu[0], real_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);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
CurrentAV->Out_Of_Service);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = CurrentAV->Out_Of_Service;
apdu_len = encode_application_boolean(&apdu[0], 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->object_property != PROP_EVENT_TIME_STAMPS) &&
(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 OctetString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
OCTETSTRING_VALUE_DESCR *CurrentAV;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index =
OctetString_Value_Instance_To_Index(wp_data->object_instance);
if (object_index < MAX_OCTETSTRING_VALUES) {
CurrentAV = &AV_Descr[object_index];
} else {
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_OCTET_STRING) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (OctetString_Value_Present_Value_Set(
wp_data->object_instance, &value.type.Octet_String,
wp_data->priority)) {
status = true;
} else 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 {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = false;
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) {
CurrentAV->Out_Of_Service = value.type.Boolean;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_DESCRIPTION:
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;
}
void OctetString_Value_Intrinsic_Reporting(uint32_t object_instance)
{
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#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 testOctetString_Value(Test *pTest)
{
BACNET_READ_PROPERTY_DATA rpdata;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
OctetString_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_OCTETSTRING_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = OctetString_Value_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_OCTETSTRING_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet OctetString Value", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testOctetString_Value);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_OCTETSTRING_VALUE */
#endif /* TEST */
+95
View File
@@ -0,0 +1,95 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 OSV_H
#define OSV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct octetstring_value_descr {
unsigned Event_State:3;
bool Out_Of_Service;
BACNET_OCTET_STRING Present_Value;
} OCTETSTRING_VALUE_DESCR;
void OctetString_Value_Property_Lists(const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool OctetString_Value_Valid_Instance(uint32_t object_instance);
unsigned OctetString_Value_Count(void);
uint32_t OctetString_Value_Index_To_Instance(unsigned index);
unsigned OctetString_Value_Instance_To_Index(uint32_t object_instance);
bool OctetString_Value_Object_Name(uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int OctetString_Value_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata);
bool OctetString_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *
wp_data);
bool OctetString_Value_Present_Value_Set(uint32_t object_instance,
BACNET_OCTET_STRING * value,
uint8_t priority);
BACNET_OCTET_STRING *OctetString_Value_Present_Value(uint32_t
object_instance);
bool OctetString_Value_Change_Of_Value(uint32_t instance);
void OctetString_Value_Change_Of_Value_Clear(uint32_t instance);
bool OctetString_Value_Encode_Value_List(uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
char *OctetString_Value_Description(uint32_t instance);
bool OctetString_Value_Description_Set(uint32_t instance,
char *new_name);
bool OctetString_Value_Out_Of_Service(uint32_t instance);
void OctetString_Value_Out_Of_Service_Set(uint32_t instance,
bool oos_flag);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void OctetString_Value_Intrinsic_Reporting(uint32_t object_instance);
void OctetString_Value_Init(void);
#ifdef TEST
#include "ctest.h"
void testOctetString_Value(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -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 -DBACNET_PROPERTY_LISTS -DTEST_OCTETSTRING_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = osv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/proplist.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = octetstring_value
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
+431
View File
@@ -0,0 +1,431 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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.
*
*********************************************************************/
/* Positiveinteger Value Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/piv.h"
#ifndef MAX_POSITIVEINTEGER_VALUES
#define MAX_POSITIVEINTEGER_VALUES 4
#endif
POSITIVEINTEGER_VALUE_DESCR PIV_Descr[MAX_POSITIVEINTEGER_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int PositiveInteger_Value_Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, PROP_UNITS, -1
};
static const int PositiveInteger_Value_Properties_Optional[] = {
PROP_OUT_OF_SERVICE, -1
};
static const int PositiveInteger_Value_Properties_Proprietary[] = { -1 };
void PositiveInteger_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = PositiveInteger_Value_Properties_Required;
}
if (pOptional) {
*pOptional = PositiveInteger_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = PositiveInteger_Value_Properties_Proprietary;
}
return;
}
void PositiveInteger_Value_Init(void)
{
unsigned i;
for (i = 0; i < MAX_POSITIVEINTEGER_VALUES; i++) {
memset(&PIV_Descr[i], 0x00, sizeof(POSITIVEINTEGER_VALUE_DESCR));
}
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool PositiveInteger_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_POSITIVEINTEGER_VALUES) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned PositiveInteger_Value_Count(void)
{
return MAX_POSITIVEINTEGER_VALUES;
}
/* 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 PositiveInteger_Value_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 PositiveInteger_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_POSITIVEINTEGER_VALUES;
if (object_instance < MAX_POSITIVEINTEGER_VALUES) {
index = object_instance;
}
return index;
}
/**
* For a given object instance-number, sets the present-value at a given
* priority 1..16.
*
* @param object_instance - object-instance number of the object
* @param value - positiveinteger value
* @param priority - priority 1..16
*
* @return true if values are within range and present-value is set.
*/
bool PositiveInteger_Value_Present_Value_Set(
uint32_t object_instance, uint32_t value, uint8_t priority)
{
unsigned index = 0;
bool status = false;
index = PositiveInteger_Value_Instance_To_Index(object_instance);
if (index < MAX_POSITIVEINTEGER_VALUES) {
PIV_Descr[index].Present_Value = value;
status = true;
}
return status;
}
uint32_t PositiveInteger_Value_Present_Value(uint32_t object_instance)
{
uint32_t value = 0;
unsigned index = 0;
index = PositiveInteger_Value_Instance_To_Index(object_instance);
if (index < MAX_POSITIVEINTEGER_VALUES) {
value = PIV_Descr[index].Present_Value;
}
return value;
}
/* note: the object name must be unique within this device */
bool PositiveInteger_Value_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_POSITIVEINTEGER_VALUES) {
sprintf(text_string, "POSITIVEINTEGER VALUE %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 PositiveInteger_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
bool state = false;
uint8_t *apdu = NULL;
POSITIVEINTEGER_VALUE_DESCR *CurrentAV;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index =
PositiveInteger_Value_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_POSITIVEINTEGER_VALUES) {
CurrentAV = &PIV_Descr[object_index];
} else {
return BACNET_STATUS_ERROR;
}
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(&apdu[0],
OBJECT_POSITIVE_INTEGER_VALUE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
PositiveInteger_Value_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_POSITIVE_INTEGER_VALUE);
break;
case PROP_PRESENT_VALUE:
apdu_len = encode_application_unsigned(&apdu[0],
PositiveInteger_Value_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);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE,
CurrentAV->Out_Of_Service);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_UNITS:
apdu_len =
encode_application_enumerated(&apdu[0], CurrentAV->Units);
break;
/* BACnet Testing Observed Incident oi00109
Positive Integer Value / Units returned wrong datatype -
missing break. Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00031 BC
135.1: 9.20.1.7 BC 135.1: 9.20.1.9 Any discussions can be
directed to edward@bac-test.com Please feel free to remove this
comment when my changes have been reviewed by all interested
parties. Say 6 months -> September 2016 */
case PROP_OUT_OF_SERVICE:
state = CurrentAV->Out_Of_Service;
apdu_len = encode_application_boolean(&apdu[0], 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->object_property != PROP_EVENT_TIME_STAMPS) &&
(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 PositiveInteger_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
POSITIVEINTEGER_VALUE_DESCR *CurrentAV;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->object_property != PROP_EVENT_TIME_STAMPS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index =
PositiveInteger_Value_Instance_To_Index(wp_data->object_instance);
if (object_index < MAX_POSITIVEINTEGER_VALUES) {
CurrentAV = &PIV_Descr[object_index];
} else {
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (PositiveInteger_Value_Present_Value_Set(
wp_data->object_instance, value.type.Unsigned_Int,
wp_data->priority)) {
status = true;
} else 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 {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = false;
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) {
CurrentAV->Out_Of_Service = value.type.Boolean;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_UNITS:
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;
}
void PositiveInteger_Value_Intrinsic_Reporting(uint32_t object_instance)
{
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#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 testPositiveInteger_Value(Test *pTest)
{
BACNET_READ_PROPERTY_DATA rpdata;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
PositiveInteger_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_POSITIVE_INTEGER_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = PositiveInteger_Value_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_POSITIVEINTEGER_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet PositiveInteger Value", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testPositiveInteger_Value);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_POSITIVEINTEGER_VALUE */
#endif /* TEST */
+95
View File
@@ -0,0 +1,95 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 PIV_H
#define PIV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct positiveinteger_value_descr {
bool Out_Of_Service:1;
uint32_t Present_Value;
uint16_t Units;
} POSITIVEINTEGER_VALUE_DESCR;
void PositiveInteger_Value_Property_Lists(const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool PositiveInteger_Value_Valid_Instance(uint32_t object_instance);
unsigned PositiveInteger_Value_Count(void);
uint32_t PositiveInteger_Value_Index_To_Instance(unsigned index);
unsigned PositiveInteger_Value_Instance_To_Index(uint32_t object_instance);
bool PositiveInteger_Value_Object_Name(uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int PositiveInteger_Value_Read_Property(BACNET_READ_PROPERTY_DATA *
rpdata);
bool PositiveInteger_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *
wp_data);
bool PositiveInteger_Value_Present_Value_Set(uint32_t object_instance,
uint32_t value,
uint8_t priority);
uint32_t PositiveInteger_Value_Present_Value(uint32_t object_instance);
bool PositiveInteger_Value_Change_Of_Value(uint32_t instance);
void PositiveInteger_Value_Change_Of_Value_Clear(uint32_t instance);
bool PositiveInteger_Value_Encode_Value_List(uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
char *PositiveInteger_Value_Description(uint32_t instance);
bool PositiveInteger_Value_Description_Set(uint32_t instance,
char *new_name);
bool PositiveInteger_Value_Out_Of_Service(uint32_t instance);
void PositiveInteger_Value_Out_Of_Service_Set(uint32_t instance,
bool oos_flag);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void PositiveInteger_Value_Intrinsic_Reporting(uint32_t object_instance);
void PositiveInteger_Value_Init(void);
#ifdef TEST
#include "ctest.h"
void testPositiveInteger_Value(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -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_POSITIVEINTEGER_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = piv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = positiveinteger_value
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
+481
View File
@@ -0,0 +1,481 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/proplist.h"
#include "bacnet/timestamp.h"
#include "bacnet/basic/object/schedule.h"
#ifndef MAX_SCHEDULES
#define MAX_SCHEDULES 4
#endif
SCHEDULE_DESCR Schedule_Descr[MAX_SCHEDULES];
static const int Schedule_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE,
PROP_EFFECTIVE_PERIOD, PROP_SCHEDULE_DEFAULT,
PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, PROP_PRIORITY_FOR_WRITING,
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_OUT_OF_SERVICE, -1 };
static const int Schedule_Properties_Optional[] = { PROP_WEEKLY_SCHEDULE, -1 };
static const int Schedule_Properties_Proprietary[] = { -1 };
void Schedule_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Schedule_Properties_Required;
}
if (pOptional) {
*pOptional = Schedule_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Schedule_Properties_Proprietary;
}
}
void Schedule_Init(void)
{
unsigned i, j;
for (i = 0; i < MAX_SCHEDULES; i++) {
/* whole year, change as neccessary */
Schedule_Descr[i].Start_Date.year = 0xFF;
Schedule_Descr[i].Start_Date.month = 1;
Schedule_Descr[i].Start_Date.day = 1;
Schedule_Descr[i].Start_Date.wday = 0xFF;
Schedule_Descr[i].End_Date.year = 0xFF;
Schedule_Descr[i].End_Date.month = 12;
Schedule_Descr[i].End_Date.day = 31;
Schedule_Descr[i].End_Date.wday = 0xFF;
for (j = 0; j < 7; j++) {
Schedule_Descr[i].Weekly_Schedule[j].TV_Count = 0;
}
Schedule_Descr[i].Present_Value = &Schedule_Descr[i].Schedule_Default;
Schedule_Descr[i].Schedule_Default.context_specific = false;
Schedule_Descr[i].Schedule_Default.tag = BACNET_APPLICATION_TAG_REAL;
Schedule_Descr[i].Schedule_Default.type.Real =
21.0; /* 21 C, room temperature */
Schedule_Descr[i].obj_prop_ref_cnt =
0; /* no references, add as needed */
Schedule_Descr[i].Priority_For_Writing = 16; /* lowest priority */
Schedule_Descr[i].Out_Of_Service = false;
}
}
bool Schedule_Valid_Instance(uint32_t object_instance)
{
unsigned int index = Schedule_Instance_To_Index(object_instance);
if (index < MAX_SCHEDULES) {
return true;
} else {
return false;
}
}
unsigned Schedule_Count(void)
{
return MAX_SCHEDULES;
}
uint32_t Schedule_Index_To_Instance(unsigned index)
{
return index;
}
unsigned Schedule_Instance_To_Index(uint32_t instance)
{
unsigned index = MAX_SCHEDULES;
if (instance < MAX_SCHEDULES) {
index = instance;
}
return index;
}
bool Schedule_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
unsigned int index;
bool status = false;
index = Schedule_Instance_To_Index(object_instance);
if (index < MAX_SCHEDULES) {
sprintf(text_string, "SCHEDULE %lu", (unsigned long)index);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* BACnet Testing Observed Incident oi00106
Out of service was not supported by Schedule object
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00032 Any discussions
can be directed to edward@bac-test.com Please feel free to remove this
comment when my changes accepted after suitable time for review by all
interested parties. Say 6 months -> September 2016 */
void Schedule_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
unsigned index = 0;
index = Schedule_Instance_To_Index(object_instance);
if (index < MAX_SCHEDULES) {
Schedule_Descr[index].Out_Of_Service = value;
}
}
int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0;
unsigned object_index = 0;
SCHEDULE_DESCR *CurrentSC;
uint8_t *apdu = NULL;
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
int i;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
object_index = Schedule_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_SCHEDULES) {
CurrentSC = &Schedule_Descr[object_index];
} else {
return BACNET_STATUS_ERROR;
}
apdu = rpdata->application_data;
switch ((int)rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_SCHEDULE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Schedule_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_SCHEDULE);
break;
case PROP_PRESENT_VALUE:
apdu_len = bacapp_encode_data(&apdu[0], CurrentSC->Present_Value);
break;
case PROP_EFFECTIVE_PERIOD:
/* BACnet Testing Observed Incident oi00110
Effective Period of Schedule object not correctly formatted
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00031 Any
discussions can be directed to edward@bac-test.com Please feel
free to remove this comment when my changes accepted after
suitable time for
review by all interested parties. Say 6 months -> September
2016 */
apdu_len =
encode_application_date(&apdu[0], &CurrentSC->Start_Date);
apdu_len +=
encode_application_date(&apdu[apdu_len], &CurrentSC->End_Date);
break;
case PROP_WEEKLY_SCHEDULE:
if (rpdata->array_index == 0) { /* count, always 7 */
apdu_len = encode_application_unsigned(&apdu[0], 7);
} else if (rpdata->array_index ==
BACNET_ARRAY_ALL) { /* full array */
int day;
for (day = 0; day < 7; day++) {
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count;
i++) {
apdu_len += bacapp_encode_time_value(&apdu[apdu_len],
&CurrentSC->Weekly_Schedule[day].Time_Values[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
}
} else if (rpdata->array_index <= 7) { /* some array element */
int day = rpdata->array_index - 1;
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
for (i = 0; i < CurrentSC->Weekly_Schedule[day].TV_Count; i++) {
apdu_len += bacapp_encode_time_value(&apdu[apdu_len],
&CurrentSC->Weekly_Schedule[day].Time_Values[i]);
}
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
} else { /* out of bounds */
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
break;
case PROP_SCHEDULE_DEFAULT:
apdu_len =
bacapp_encode_data(&apdu[0], &CurrentSC->Schedule_Default);
break;
case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES:
for (i = 0; i < CurrentSC->obj_prop_ref_cnt; i++) {
apdu_len += bacapp_encode_device_obj_property_ref(
&apdu[apdu_len], &CurrentSC->Object_Property_References[i]);
}
break;
case PROP_PRIORITY_FOR_WRITING:
apdu_len = encode_application_unsigned(
&apdu[0], CurrentSC->Priority_For_Writing);
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], RELIABILITY_NO_FAULT_DETECTED);
break;
case PROP_OUT_OF_SERVICE:
/* BACnet Testing Observed Incident oi00106
Out of service was not supported by Schedule object
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00032 Any
discussions can be directed to edward@bac-test.com Please feel
free to remove this comment when my changes accepted after
suitable time for
review by all interested parties. Say 6 months -> September
2016 */
apdu_len =
encode_application_boolean(&apdu[0], CurrentSC->Out_Of_Service);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
if ((apdu_len >= 0) && (rpdata->object_property != PROP_WEEKLY_SCHEDULE) &&
(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;
}
bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
/* Ed->Steve, I know that initializing stack values used to be 'safer', but
warnings in latest compilers indicate when uninitialized values are being
used, and I think that the warnings are more useful to reveal bad code
flow than the "safety: of pre-intializing variables. Please give this
some thought let me know if you agree we should start to remove
initializations */
unsigned object_index;
bool status = false; /* return value */
int len;
BACNET_APPLICATION_DATA_VALUE value;
/* BACnet Testing Observed Incident oi00106
Out of service was not supported by Schedule object
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00032 Any
discussions can be directed to edward@bac-test.com Please feel free to
remove this comment when my changes accepted after suitable time for
review by all interested parties. Say 6 months -> September 2016 */
/* 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;
}
object_index = Schedule_Instance_To_Index(wp_data->object_instance);
if (object_index >= MAX_SCHEDULES) {
return false;
}
switch ((int)wp_data->object_property) {
case PROP_OUT_OF_SERVICE:
/* BACnet Testing Observed Incident oi00106
Out of service was not supported by Schedule object
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00032 Any
discussions can be directed to edward@bac-test.com Please feel
free to remove this comment when my changes accepted after
suitable time for
review by all interested parties. Say 6 months -> September
2016 */
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Schedule_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_PRESENT_VALUE:
case PROP_EFFECTIVE_PERIOD:
case PROP_WEEKLY_SCHEDULE:
case PROP_SCHEDULE_DEFAULT:
case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES:
case PROP_PRIORITY_FOR_WRITING:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
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;
}
bool Schedule_In_Effective_Period(SCHEDULE_DESCR *desc, BACNET_DATE *date)
{
bool res = false;
if (desc && date) {
if (datetime_wildcard_compare_date(&desc->Start_Date, date) <= 0 &&
datetime_wildcard_compare_date(&desc->End_Date, date) >= 0) {
res = true;
}
}
return res;
}
void Schedule_Recalculate_PV(
SCHEDULE_DESCR *desc, BACNET_WEEKDAY wday, BACNET_TIME *time)
{
int i;
desc->Present_Value = NULL;
/* for future development, here should be the loop for Exception Schedule */
/* Just a note to developers: We have a paying customer who has asked us to
fully implement the Schedule Object. In good spirit, they have agreed to
allow us to release the code we develop back to the Open Source community
after a 6-12 month waiting period. However, if you are about to work on
this yourself, please ping us at info@connect-ex.com, we may be able to
broker an early release on a case-by-case basis. */
for (i = 0; i < desc->Weekly_Schedule[wday - 1].TV_Count &&
desc->Present_Value == NULL;
i++) {
int diff = datetime_wildcard_compare_time(
time, &desc->Weekly_Schedule[wday - 1].Time_Values[i].Time);
if (diff >= 0 &&
desc->Weekly_Schedule[wday - 1].Time_Values[i].Value.tag !=
BACNET_APPLICATION_TAG_NULL) {
desc->Present_Value =
&desc->Weekly_Schedule[wday - 1].Time_Values[i].Value;
}
}
if (desc->Present_Value == NULL) {
desc->Present_Value = &desc->Schedule_Default;
}
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testSchedule(Test *pTest)
{
BACNET_READ_PROPERTY_DATA rpdata;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
Schedule_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_SCHEDULE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Schedule_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_SCHEDULE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Schedule", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testSchedule);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_SCHEDULE */
#endif /* TEST */
+108
View File
@@ -0,0 +1,108 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* 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 SCHEDULE_H
#define SCHEDULE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacapp.h"
#include "bacnet/datetime.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/bactimevalue.h"
#ifndef BACNET_WEEKLY_SCHEDULE_SIZE
#define BACNET_WEEKLY_SCHEDULE_SIZE 8 /* maximum number of data points for each day */
#endif
#ifndef BACNET_SCHEDULE_OBJ_PROP_REF_SIZE
#define BACNET_SCHEDULE_OBJ_PROP_REF_SIZE 4 /* maximum number of obj prop references */
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct bacnet_daily_schedule {
BACNET_TIME_VALUE Time_Values[BACNET_WEEKLY_SCHEDULE_SIZE];
uint16_t TV_Count; /* the number of time values actually used */
} BACNET_DAILY_SCHEDULE;
typedef struct schedule {
/* Effective Period: Start and End Date */
BACNET_DATE Start_Date;
BACNET_DATE End_Date;
/* Properties concerning Present Value */
BACNET_DAILY_SCHEDULE Weekly_Schedule[7];
BACNET_APPLICATION_DATA_VALUE Schedule_Default;
BACNET_APPLICATION_DATA_VALUE *Present_Value; /* must be set to a valid value
* default is Schedule_Default */
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE
Object_Property_References[BACNET_SCHEDULE_OBJ_PROP_REF_SIZE];
uint8_t obj_prop_ref_cnt; /* actual number of obj_prop references */
uint8_t Priority_For_Writing; /* (1..16) */
bool Out_Of_Service;
} SCHEDULE_DESCR;
void Schedule_Property_Lists(const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Schedule_Valid_Instance(uint32_t object_instance);
unsigned Schedule_Count(void);
uint32_t Schedule_Index_To_Instance(unsigned index);
unsigned Schedule_Instance_To_Index(uint32_t instance);
void Schedule_Init(void);
void Schedule_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
bool Schedule_Out_Of_Service(
uint32_t object_instance);
bool Schedule_Object_Name(uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata);
bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data);
/* utility functions for calculating current Present Value
* if Exception Schedule is to be added, these functions must take that into account */
bool Schedule_In_Effective_Period(SCHEDULE_DESCR * desc,
BACNET_DATE * date);
void Schedule_Recalculate_PV(SCHEDULE_DESCR * desc,
BACNET_WEEKDAY wday,
BACNET_TIME * time);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -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_SCHEDULE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = schedule.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactimevalue.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = schedule
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
File diff suppressed because it is too large Load Diff
+200
View File
@@ -0,0 +1,200 @@
/**************************************************************************
*
* Copyright (C) 2009 Peter Mc Shane
*
* 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 TRENDLOG_H
#define TRENDLOG_H
#include <stdbool.h>
#include <stdint.h>
#include <time.h> /* for time_t */
#include "bacnet/bacdef.h"
#include "bacnet/cov.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Error code for Trend Log storage */
typedef struct tl_error {
uint16_t usClass;
uint16_t usCode;
} TL_ERROR;
/* Bit string of up to 32 bits for Trend Log storage */
typedef struct tl_bits {
uint8_t ucLen; /* bytes used in upper nibble/bits free in lower nibble */
uint8_t ucStore[4];
} TL_BITS;
/* Storage structure for Trend Log data
*
* Note. I've tried to minimise the storage requirements here
* as the memory requirements for logging in embedded
* implementations are frequently a big issue. For PC or
* embedded Linux type setupz this may seem like overkill
* but if you have limited memory and need to squeeze as much
* logging capacity as possible every little byte counts!
*/
typedef struct tl_data_record {
time_t tTimeStamp; /* When the event occurred */
uint8_t ucRecType; /* What type of Event */
uint8_t ucStatus; /* Optional Status for read value in b0-b2, b7 = 1 if status is used */
union {
uint8_t ucLogStatus; /* Change of log state flags */
uint8_t ucBoolean; /* Stored boolean value */
float fReal; /* Stored floating point value */
uint32_t ulEnum; /* Stored enumerated value - max 32 bits */
uint32_t ulUValue; /* Stored unsigned value - max 32 bits */
int32_t lSValue; /* Stored signed value - max 32 bits */
TL_BITS Bits; /* Stored bitstring - max 32 bits */
TL_ERROR Error; /* Two part error class/code combo */
float fTime; /* Interval value for change of time - seconds */
} Datum;
} TL_DATA_REC;
#define TL_T_START_WILD 1 /* Start time is wild carded */
#define TL_T_STOP_WILD 2 /* Stop Time is wild carded */
#define TL_MAX_ENTRIES 1000 /* Entries per datalog */
/* Structure containing config and status info for a Trend Log */
typedef struct tl_log_info {
bool bEnable; /* Trend log is active when this is true */
BACNET_DATE_TIME StartTime; /* BACnet format start time */
time_t tStartTime; /* Local time working copy of start time */
BACNET_DATE_TIME StopTime; /* BACnet format stop time */
time_t tStopTime; /* Local time working copy of stop time */
uint8_t ucTimeFlags; /* Shorthand info on times */
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE Source; /* Where the data comes from */
uint32_t ulLogInterval; /* Time between entries in seconds */
bool bStopWhenFull; /* Log halts when full if true */
uint32_t ulRecordCount; /* Count of items currently in the buffer */
uint32_t ulTotalRecordCount; /* Count of all items that have ever been inserted into the buffer */
BACNET_LOGGING_TYPE LoggingType; /* Polled/cov/triggered */
bool bAlignIntervals; /* If true align to the clock */
uint32_t ulIntervalOffset; /* Offset from start of period for taking reading in seconds */
bool bTrigger; /* Set to 1 to cause a reading to be taken */
int iIndex; /* Current insertion point */
time_t tLastDataTime;
} TL_LOG_INFO;
/*
* Data types associated with a BACnet Log Record. We use these for managing the
* log buffer but they are also the tag numbers to use when encoding/decoding
* the log datum field.
*/
#define TL_TYPE_STATUS 0
#define TL_TYPE_BOOL 1
#define TL_TYPE_REAL 2
#define TL_TYPE_ENUM 3
#define TL_TYPE_UNSIGN 4
#define TL_TYPE_SIGN 5
#define TL_TYPE_BITS 6
#define TL_TYPE_NULL 7
#define TL_TYPE_ERROR 8
#define TL_TYPE_DELTA 9
#define TL_TYPE_ANY 10 /* We don't support this particular can of worms! */
void Trend_Log_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Trend_Log_Valid_Instance(
uint32_t object_instance);
unsigned Trend_Log_Count(
void);
uint32_t Trend_Log_Index_To_Instance(
unsigned index);
unsigned Trend_Log_Instance_To_Index(
uint32_t instance);
bool Trend_Log_Object_Instance_Add(
uint32_t instance);
bool Trend_Log_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
int Trend_Log_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Trend_Log_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
void Trend_Log_Init(
void);
void TL_Insert_Status_Rec(
int iLog,
BACNET_LOG_STATUS eStatus,
bool bState);
bool TL_Is_Enabled(
int iLog);
time_t TL_BAC_Time_To_Local(
BACNET_DATE_TIME * SourceTime);
void TL_Local_Time_To_BAC(
BACNET_DATE_TIME * DestTime,
time_t SourceTime);
int TL_encode_entry(
uint8_t * apdu,
int iLog,
int iEntry);
int TL_encode_by_position(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
int TL_encode_by_sequence(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
int TL_encode_by_time(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
bool TrendLogGetRRInfo(
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
RR_PROP_INFO * pInfo); /* Where to put the information */
int rr_trend_log_encode(
uint8_t * apdu,
BACNET_READ_RANGE_DATA * pRequest);
void trend_log_timer(
uint16_t uSeconds);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif