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:
@@ -0,0 +1,50 @@
|
||||
#Makefile to build BACnet Library with GCC
|
||||
|
||||
# tools - only if you need them.
|
||||
# Most platforms have this already defined
|
||||
# CC = gcc
|
||||
# AR = ar
|
||||
# MAKE = make
|
||||
# SIZE = size
|
||||
#
|
||||
# Assumes rm and cp are available
|
||||
|
||||
# These might be already defined in an previous Makefile
|
||||
BACNET_PORT ?= linux
|
||||
BACNET_SRC_DIR ?= $(realpath .)
|
||||
BACNET_PORT_DIR ?= $(realpath ../ports/$(BACNET_PORT))
|
||||
BACNET_PORT_SRC ?= \
|
||||
$(BACNET_PORT_DIR)/bip-init.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bvlc.c \
|
||||
$(BACNET_SRC_DIR)/bacnet/datalink/bip.c
|
||||
|
||||
# include file search paths
|
||||
BACNET_INCLUDES = -I$(BACNET_SRC_DIR) -I$(BACNET_PORT_DIR)
|
||||
CFLAGS += $(BACNET_INCLUDES)
|
||||
|
||||
BACNET_SRC ?= main.c \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/binding/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/service/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/npdu/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/sys/*.c) \
|
||||
$(wildcard $(BACNET_SRC_DIR)/bacnet/basic/tsm/*.c)
|
||||
|
||||
ifneq (,$(findstring -DBAC_UCI,$(BACNET_DEFINES)))
|
||||
OBJS += $(patsubst %.c,%.o,$(wildcard basic/ucix/*.c))
|
||||
endif
|
||||
|
||||
all: $(OBJS) Makefile
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core $(OBJS) $(LIBRARY)
|
||||
|
||||
include: .depend
|
||||
@@ -32,10 +32,10 @@
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include "bacenum.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacdef.h"
|
||||
#include "abort.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/abort.h"
|
||||
|
||||
/** @file abort.c Abort Encoding/Decoding */
|
||||
/* Helper function to avoid needing additional entries in service data
|
||||
@@ -73,16 +73,17 @@ BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
}
|
||||
|
||||
/* encode service */
|
||||
int abort_encode_apdu(uint8_t *apdu, uint8_t invoke_id, uint8_t abort_reason,
|
||||
bool server)
|
||||
int abort_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
if (server)
|
||||
if (server) {
|
||||
apdu[0] = PDU_TYPE_ABORT | 1;
|
||||
else
|
||||
} else {
|
||||
apdu[0] = PDU_TYPE_ABORT;
|
||||
}
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = abort_reason;
|
||||
apdu_len = 3;
|
||||
@@ -93,16 +94,18 @@ int abort_encode_apdu(uint8_t *apdu, uint8_t invoke_id, uint8_t abort_reason,
|
||||
|
||||
#if !BACNET_SVC_SERVER
|
||||
/* decode the service request only */
|
||||
int abort_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
uint8_t *invoke_id, uint8_t *abort_reason)
|
||||
int abort_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id, uint8_t *abort_reason)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (apdu_len > 0) {
|
||||
if (invoke_id)
|
||||
if (invoke_id) {
|
||||
*invoke_id = apdu[0];
|
||||
if (abort_reason)
|
||||
}
|
||||
if (abort_reason) {
|
||||
*abort_reason = apdu[1];
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -115,8 +118,11 @@ int abort_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
#include "ctest.h"
|
||||
|
||||
/* decode the whole APDU - mainly used for unit testing */
|
||||
int abort_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
uint8_t *abort_reason, bool *server)
|
||||
int abort_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
uint8_t *abort_reason,
|
||||
bool *server)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -131,18 +137,18 @@ int abort_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
else
|
||||
*server = false;
|
||||
if (apdu_len > 1) {
|
||||
len = abort_decode_service_request(&apdu[1], apdu_len - 1,
|
||||
invoke_id, abort_reason);
|
||||
len = abort_decode_service_request(
|
||||
&apdu[1], apdu_len - 1, invoke_id, abort_reason);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void testAbortAPDU(Test *pTest, uint8_t invoke_id, uint8_t abort_reason,
|
||||
bool server)
|
||||
void testAbortAPDU(
|
||||
Test *pTest, uint8_t invoke_id, uint8_t abort_reason, bool server)
|
||||
{
|
||||
uint8_t apdu[480] = {0};
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t test_invoke_id = 0;
|
||||
@@ -152,8 +158,8 @@ void testAbortAPDU(Test *pTest, uint8_t invoke_id, uint8_t abort_reason,
|
||||
len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server);
|
||||
apdu_len = len;
|
||||
ct_test(pTest, len != 0);
|
||||
len = abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_abort_reason, &test_server);
|
||||
len = abort_decode_apdu(
|
||||
&apdu[0], apdu_len, &test_invoke_id, &test_abort_reason, &test_server);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_abort_reason == abort_reason);
|
||||
@@ -164,7 +170,7 @@ void testAbortAPDU(Test *pTest, uint8_t invoke_id, uint8_t abort_reason,
|
||||
|
||||
void testAbort(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[480] = {0};
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
@@ -177,8 +183,8 @@ void testAbort(Test *pTest)
|
||||
len = abort_encode_apdu(&apdu[0], invoke_id, abort_reason, server);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
len = abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_abort_reason, &test_server);
|
||||
len = abort_decode_apdu(
|
||||
&apdu[0], apdu_len, &test_invoke_id, &test_abort_reason, &test_server);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_abort_reason == abort_reason);
|
||||
@@ -186,18 +192,18 @@ void testAbort(Test *pTest)
|
||||
|
||||
/* change type to get negative response */
|
||||
apdu[0] = PDU_TYPE_REJECT;
|
||||
len = abort_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_abort_reason, &test_server);
|
||||
len = abort_decode_apdu(
|
||||
&apdu[0], apdu_len, &test_invoke_id, &test_abort_reason, &test_server);
|
||||
ct_test(pTest, len == -1);
|
||||
|
||||
/* test NULL APDU */
|
||||
len = abort_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_abort_reason,
|
||||
&test_server);
|
||||
len = abort_decode_apdu(
|
||||
NULL, apdu_len, &test_invoke_id, &test_abort_reason, &test_server);
|
||||
ct_test(pTest, len == -1);
|
||||
|
||||
/* force a zero length */
|
||||
len = abort_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_abort_reason,
|
||||
&test_server);
|
||||
len = abort_decode_apdu(
|
||||
&apdu[0], 0, &test_invoke_id, &test_abort_reason, &test_server);
|
||||
ct_test(pTest, len == 0);
|
||||
|
||||
/* check them all... */
|
||||
@@ -0,0 +1,66 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 ABORT_H
|
||||
#define ABORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_ABORT_REASON abort_convert_error_code(
|
||||
BACNET_ERROR_CODE error_code);
|
||||
|
||||
int abort_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
uint8_t abort_reason,
|
||||
bool server);
|
||||
|
||||
int abort_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
uint8_t * abort_reason);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
int abort_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
uint8_t * abort_reason,
|
||||
bool * server);
|
||||
|
||||
void testAbort(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -25,10 +25,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "access_rule.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacnet/access_rule.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
int bacapp_encode_access_rule(uint8_t* apdu, BACNET_ACCESS_RULE* rule)
|
||||
int bacapp_encode_access_rule(uint8_t *apdu, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -37,12 +37,13 @@ int bacapp_encode_access_rule(uint8_t* apdu, BACNET_ACCESS_RULE* rule)
|
||||
apdu_len += len;
|
||||
|
||||
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
||||
len = bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 1,
|
||||
&rule->time_range);
|
||||
if (len > 0)
|
||||
len = bacapp_encode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 1, &rule->time_range);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
len =
|
||||
@@ -50,12 +51,13 @@ int bacapp_encode_access_rule(uint8_t* apdu, BACNET_ACCESS_RULE* rule)
|
||||
apdu_len += len;
|
||||
|
||||
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
||||
len = bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 3,
|
||||
&rule->location);
|
||||
if (len > 0)
|
||||
len = bacapp_encode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 3, &rule->location);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
len = encode_context_boolean(&apdu[apdu_len], 4, rule->enable);
|
||||
@@ -64,8 +66,8 @@ int bacapp_encode_access_rule(uint8_t* apdu, BACNET_ACCESS_RULE* rule)
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_access_rule(uint8_t* apdu, uint8_t tag_number,
|
||||
BACNET_ACCESS_RULE* rule)
|
||||
int bacapp_encode_context_access_rule(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -82,69 +84,79 @@ int bacapp_encode_context_access_rule(uint8_t* apdu, uint8_t tag_number,
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_access_rule(uint8_t* apdu, BACNET_ACCESS_RULE* rule)
|
||||
int bacapp_decode_access_rule(uint8_t *apdu, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 0,
|
||||
&rule->time_range_specifier);
|
||||
if (len < 0)
|
||||
len = decode_context_enumerated(
|
||||
&apdu[apdu_len], 0, &rule->time_range_specifier);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = bacapp_decode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 1, &rule->time_range);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 2,
|
||||
&rule->location_specifier);
|
||||
if (len < 0)
|
||||
len = decode_context_enumerated(
|
||||
&apdu[apdu_len], 2, &rule->location_specifier);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 3)) {
|
||||
len = bacapp_decode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 3, &rule->location);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 4)) {
|
||||
len = decode_context_boolean2(&apdu[apdu_len], 4, &rule->enable);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_access_rule(uint8_t* apdu, uint8_t tag_number,
|
||||
BACNET_ACCESS_RULE* rule)
|
||||
int bacapp_decode_context_access_rule(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -0,0 +1,76 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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_RULE_H
|
||||
#define ACCESS_RULE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
|
||||
typedef enum {
|
||||
TIME_RANGE_SPECIFIER_SPECIFIED = 0,
|
||||
TIME_RANGE_SPECIFIER_ALWAYS = 1
|
||||
} BACNET_ACCESS_RULE_TIME_RANGE_SPECIFIER;
|
||||
|
||||
typedef enum {
|
||||
LOCATION_SPECIFIER_SPECIFIED = 0,
|
||||
LOCATION_SPECIFIER_ALL = 1
|
||||
} BACNET_ACCESS_RULE_LOCATION_SPECIFIER;
|
||||
|
||||
typedef struct {
|
||||
BACNET_ACCESS_RULE_TIME_RANGE_SPECIFIER time_range_specifier;
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE time_range;
|
||||
BACNET_ACCESS_RULE_LOCATION_SPECIFIER location_specifier;
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE location;
|
||||
bool enable;
|
||||
} BACNET_ACCESS_RULE;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_access_rule(
|
||||
uint8_t * apdu,
|
||||
BACNET_ACCESS_RULE * rule);
|
||||
int bacapp_encode_context_access_rule(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ACCESS_RULE * rule);
|
||||
int bacapp_decode_access_rule(
|
||||
uint8_t * apdu,
|
||||
BACNET_ACCESS_RULE * rule);
|
||||
int bacapp_decode_context_access_rule(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ACCESS_RULE * rule);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -31,8 +31,9 @@
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include "alarm_ack.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/alarm_ack.h"
|
||||
|
||||
/** @file alarm_ack.c Handles Event Notifications (ACKs) */
|
||||
|
||||
@@ -41,10 +42,10 @@
|
||||
** Creates an Unconfirmed Event Notification APDU
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_ALARM_ACK_DATA *data)
|
||||
int alarm_ack_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_ALARM_ACK_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
@@ -68,33 +69,33 @@ int alarm_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
****************************************************/
|
||||
int alarm_ack_encode_service_request(uint8_t *apdu, BACNET_ALARM_ACK_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 0,
|
||||
data->ackProcessIdentifier);
|
||||
len = encode_context_unsigned(
|
||||
&apdu[apdu_len], 0, data->ackProcessIdentifier);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_context_object_id(&apdu[apdu_len], 1,
|
||||
(int)data->eventObjectIdentifier.type,
|
||||
data->eventObjectIdentifier.instance);
|
||||
(int)data->eventObjectIdentifier.type,
|
||||
data->eventObjectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 2,
|
||||
data->eventStateAcked);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[apdu_len], 2, data->eventStateAcked);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_context_timestamp(&apdu[apdu_len], 3,
|
||||
&data->eventTimeStamp);
|
||||
len = bacapp_encode_context_timestamp(
|
||||
&apdu[apdu_len], 3, &data->eventTimeStamp);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_context_character_string(&apdu[apdu_len], 4,
|
||||
&data->ackSource);
|
||||
len = encode_context_character_string(
|
||||
&apdu[apdu_len], 4, &data->ackSource);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_context_timestamp(&apdu[apdu_len], 5,
|
||||
&data->ackTimeStamp);
|
||||
len = bacapp_encode_context_timestamp(
|
||||
&apdu[apdu_len], 5, &data->ackTimeStamp);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
@@ -106,8 +107,8 @@ int alarm_ack_encode_service_request(uint8_t *apdu, BACNET_ALARM_ACK_DATA *data)
|
||||
** Decodes the service data part of Event Notification
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ALARM_ACK_DATA *data)
|
||||
int alarm_ack_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ALARM_ACK_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int section_len;
|
||||
@@ -116,15 +117,17 @@ int alarm_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* unused parameter */
|
||||
apdu_len = apdu_len;
|
||||
|
||||
if (-1 == (section_len = decode_context_unsigned(
|
||||
&apdu[len], 0, &data->ackProcessIdentifier))) {
|
||||
if (-1 ==
|
||||
(section_len = decode_context_unsigned(
|
||||
&apdu[len], 0, &data->ackProcessIdentifier))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
if (-1 == (section_len = decode_context_object_id(
|
||||
&apdu[len], 1, &data->eventObjectIdentifier.type,
|
||||
&data->eventObjectIdentifier.instance))) {
|
||||
if (-1 ==
|
||||
(section_len = decode_context_object_id(&apdu[len], 1,
|
||||
&data->eventObjectIdentifier.type,
|
||||
&data->eventObjectIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
@@ -136,20 +139,23 @@ int alarm_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
data->eventStateAcked = (BACNET_EVENT_STATE)enumValue;
|
||||
len += section_len;
|
||||
|
||||
if (-1 == (section_len = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 3, &data->eventTimeStamp))) {
|
||||
if (-1 ==
|
||||
(section_len = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 3, &data->eventTimeStamp))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
if (-1 == (section_len = decode_context_character_string(
|
||||
&apdu[len], 4, &data->ackSource))) {
|
||||
if (-1 ==
|
||||
(section_len = decode_context_character_string(
|
||||
&apdu[len], 4, &data->ackSource))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
if (-1 == (section_len = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 5, &data->ackTimeStamp))) {
|
||||
if (-1 ==
|
||||
(section_len = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 5, &data->ackTimeStamp))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
@@ -192,35 +198,45 @@ void testAlarmAck(Test *pTest)
|
||||
|
||||
ct_test(pTest, inLen == outLen);
|
||||
|
||||
ct_test(pTest, testAlarmAckIn.ackProcessIdentifier ==
|
||||
testAlarmAckOut.ackProcessIdentifier);
|
||||
|
||||
ct_test(pTest, testAlarmAckIn.ackTimeStamp.tag ==
|
||||
testAlarmAckOut.ackTimeStamp.tag);
|
||||
ct_test(pTest, testAlarmAckIn.ackTimeStamp.value.sequenceNum ==
|
||||
testAlarmAckOut.ackTimeStamp.value.sequenceNum);
|
||||
|
||||
ct_test(pTest, testAlarmAckIn.ackProcessIdentifier ==
|
||||
testAlarmAckOut.ackProcessIdentifier);
|
||||
|
||||
ct_test(pTest, testAlarmAckIn.eventObjectIdentifier.instance ==
|
||||
testAlarmAckOut.eventObjectIdentifier.instance);
|
||||
ct_test(pTest, testAlarmAckIn.eventObjectIdentifier.type ==
|
||||
testAlarmAckOut.eventObjectIdentifier.type);
|
||||
|
||||
ct_test(pTest, testAlarmAckIn.eventTimeStamp.tag ==
|
||||
testAlarmAckOut.eventTimeStamp.tag);
|
||||
ct_test(pTest, testAlarmAckIn.eventTimeStamp.value.time.hour ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.hour);
|
||||
ct_test(pTest, testAlarmAckIn.eventTimeStamp.value.time.min ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.min);
|
||||
ct_test(pTest, testAlarmAckIn.eventTimeStamp.value.time.sec ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.sec);
|
||||
ct_test(pTest, testAlarmAckIn.eventTimeStamp.value.time.hundredths ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.hundredths);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.ackProcessIdentifier ==
|
||||
testAlarmAckOut.ackProcessIdentifier);
|
||||
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventStateAcked == testAlarmAckOut.eventStateAcked);
|
||||
testAlarmAckIn.ackTimeStamp.tag == testAlarmAckOut.ackTimeStamp.tag);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.ackTimeStamp.value.sequenceNum ==
|
||||
testAlarmAckOut.ackTimeStamp.value.sequenceNum);
|
||||
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.ackProcessIdentifier ==
|
||||
testAlarmAckOut.ackProcessIdentifier);
|
||||
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventObjectIdentifier.instance ==
|
||||
testAlarmAckOut.eventObjectIdentifier.instance);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventObjectIdentifier.type ==
|
||||
testAlarmAckOut.eventObjectIdentifier.type);
|
||||
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventTimeStamp.tag ==
|
||||
testAlarmAckOut.eventTimeStamp.tag);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventTimeStamp.value.time.hour ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.hour);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventTimeStamp.value.time.min ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.min);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventTimeStamp.value.time.sec ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.sec);
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventTimeStamp.value.time.hundredths ==
|
||||
testAlarmAckOut.eventTimeStamp.value.time.hundredths);
|
||||
|
||||
ct_test(pTest,
|
||||
testAlarmAckIn.eventStateAcked == testAlarmAckOut.eventStateAcked);
|
||||
}
|
||||
|
||||
#ifdef TEST_ALARM_ACK
|
||||
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 John Minack
|
||||
*
|
||||
* 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 ALARM_ACK_H_
|
||||
#define ALARM_ACK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t ackProcessIdentifier;
|
||||
BACNET_OBJECT_ID eventObjectIdentifier;
|
||||
BACNET_EVENT_STATE eventStateAcked;
|
||||
BACNET_TIMESTAMP eventTimeStamp;
|
||||
BACNET_CHARACTER_STRING ackSource;
|
||||
BACNET_TIMESTAMP ackTimeStamp;
|
||||
} BACNET_ALARM_ACK_DATA;
|
||||
|
||||
/* return +1 if alarm was acknowledged
|
||||
return -1 if any error occurred
|
||||
return -2 abort */
|
||||
typedef int (
|
||||
*alarm_ack_function) (
|
||||
BACNET_ALARM_ACK_DATA * alarmack_data,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Creates a Alarm Acknowledge APDU
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Encodes the service data part of Alarm Acknowledge
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_service_request(
|
||||
uint8_t * apdu,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Decodes the service data part of Alarm Acknowledge
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* ALARM_ACK_H_ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 APDU_H
|
||||
#define APDU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct _confirmed_service_data {
|
||||
bool segmented_message;
|
||||
bool more_follows;
|
||||
bool segmented_response_accepted;
|
||||
int max_segs;
|
||||
int max_resp;
|
||||
uint8_t invoke_id;
|
||||
uint8_t sequence_number;
|
||||
uint8_t proposed_window_number;
|
||||
} BACNET_CONFIRMED_SERVICE_DATA;
|
||||
|
||||
typedef struct _confirmed_service_ack_data {
|
||||
bool segmented_message;
|
||||
bool more_follows;
|
||||
uint8_t invoke_id;
|
||||
uint8_t sequence_number;
|
||||
uint8_t proposed_window_number;
|
||||
} BACNET_CONFIRMED_SERVICE_ACK_DATA;
|
||||
|
||||
#endif
|
||||
+129
-103
@@ -32,16 +32,16 @@
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include "bacenum.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacdef.h"
|
||||
#include "arf.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/arf.h"
|
||||
|
||||
/** @file arf.c Atomic Read File */
|
||||
|
||||
/* encode service */
|
||||
int arf_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
@@ -79,8 +79,8 @@ int arf_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int arf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
@@ -92,8 +92,9 @@ int arf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
if (apdu_len && data) {
|
||||
len =
|
||||
decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_object_id(&apdu[len], &type, &data->object_instance);
|
||||
data->object_type = (BACNET_OBJECT_TYPE)type;
|
||||
if (decode_is_opening_tag_number(&apdu[len], 0)) {
|
||||
@@ -101,23 +102,26 @@ int arf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartPosition */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.stream.fileStartPosition);
|
||||
&data->type.stream.fileStartPosition);
|
||||
/* requestedOctetCount */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_unsigned(&apdu[len], len_value_type,
|
||||
&data->type.stream.requestedOctetCount);
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 0))
|
||||
&data->type.stream.requestedOctetCount);
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 0)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
|
||||
@@ -125,47 +129,56 @@ int arf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartRecord */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.record.fileStartRecord);
|
||||
}
|
||||
len += decode_signed(
|
||||
&apdu[len], len_value_type, &data->type.record.fileStartRecord);
|
||||
/* RecordCount */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
len += decode_unsigned(&apdu[len], len_value_type,
|
||||
&data->type.record.RecordCount);
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1))
|
||||
}
|
||||
len += decode_unsigned(
|
||||
&apdu[len], len_value_type, &data->type.record.RecordCount);
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int arf_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu)
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
|
||||
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) {
|
||||
return -1;
|
||||
}
|
||||
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
|
||||
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
|
||||
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE)
|
||||
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
|
||||
return -1;
|
||||
}
|
||||
offset = 4;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
@@ -177,8 +190,8 @@ int arf_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
}
|
||||
|
||||
/* encode service */
|
||||
int arf_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_ack_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
uint32_t i = 0;
|
||||
@@ -196,8 +209,8 @@ int arf_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += encode_application_signed(
|
||||
&apdu[apdu_len], data->type.stream.fileStartPosition);
|
||||
apdu_len += encode_application_octet_string(&apdu[apdu_len],
|
||||
&data->fileData[0]);
|
||||
apdu_len += encode_application_octet_string(
|
||||
&apdu[apdu_len], &data->fileData[0]);
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
@@ -221,8 +234,8 @@ int arf_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int arf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_ack_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
@@ -244,23 +257,23 @@ int arf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartPosition */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.stream.fileStartPosition);
|
||||
&data->type.stream.fileStartPosition);
|
||||
/* fileData */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
|
||||
return -1;
|
||||
}
|
||||
decoded_len = decode_octet_string(&apdu[len], len_value_type,
|
||||
&data->fileData[0]);
|
||||
decoded_len = decode_octet_string(
|
||||
&apdu[len], len_value_type, &data->fileData[0]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
@@ -275,33 +288,33 @@ int arf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartRecord */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.record.fileStartRecord);
|
||||
len += decode_signed(
|
||||
&apdu[len], len_value_type, &data->type.record.fileStartRecord);
|
||||
/* returnedRecordCount */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_unsigned(&apdu[len], len_value_type,
|
||||
&data->type.record.RecordCount);
|
||||
len += decode_unsigned(
|
||||
&apdu[len], len_value_type, &data->type.record.RecordCount);
|
||||
for (i = 0; i < data->type.record.RecordCount; i++) {
|
||||
/* fileData */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
|
||||
return -1;
|
||||
}
|
||||
decoded_len = decode_octet_string(&apdu[len], len_value_type,
|
||||
&data->fileData[i]);
|
||||
decoded_len = decode_octet_string(
|
||||
&apdu[len], len_value_type, &data->fileData[i]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
@@ -320,25 +333,30 @@ int arf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
return len;
|
||||
}
|
||||
|
||||
int arf_ack_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
int arf_ack_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu)
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
|
||||
if (apdu[0] != PDU_TYPE_COMPLEX_ACK) {
|
||||
return -1;
|
||||
}
|
||||
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
|
||||
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE)
|
||||
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
|
||||
return -1;
|
||||
}
|
||||
offset = 3;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len = arf_ack_decode_service_request(&apdu[offset], apdu_len - offset,
|
||||
data);
|
||||
len = arf_ack_decode_service_request(
|
||||
&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -349,11 +367,11 @@ int arf_ack_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAtomicReadFileAckAccess(Test *pTest,
|
||||
BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
void testAtomicReadFileAckAccess(
|
||||
Test *pTest, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA test_data = {0};
|
||||
uint8_t apdu[480] = {0};
|
||||
BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 };
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 128;
|
||||
@@ -369,40 +387,45 @@ void testAtomicReadFileAckAccess(Test *pTest,
|
||||
ct_test(pTest, test_data.endOfFile == data->endOfFile);
|
||||
ct_test(pTest, test_data.access == data->access);
|
||||
if (test_data.access == FILE_STREAM_ACCESS) {
|
||||
ct_test(pTest, test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest, octetstring_length(&test_data.fileData[0]) ==
|
||||
octetstring_length(&data->fileData[0]));
|
||||
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData[0]),
|
||||
octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&test_data.fileData[0])) == 0);
|
||||
ct_test(pTest,
|
||||
test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest,
|
||||
octetstring_length(&test_data.fileData[0]) ==
|
||||
octetstring_length(&data->fileData[0]));
|
||||
ct_test(pTest,
|
||||
memcmp(octetstring_value(&test_data.fileData[0]),
|
||||
octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&test_data.fileData[0])) == 0);
|
||||
} else if (test_data.access == FILE_RECORD_ACCESS) {
|
||||
ct_test(pTest, test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest, test_data.type.record.RecordCount ==
|
||||
data->type.record.RecordCount);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.RecordCount == data->type.record.RecordCount);
|
||||
for (i = 0; i < data->type.record.RecordCount; i++) {
|
||||
ct_test(pTest, octetstring_length(&test_data.fileData[i]) ==
|
||||
octetstring_length(&data->fileData[i]));
|
||||
ct_test(pTest,
|
||||
memcmp(octetstring_value(&test_data.fileData[i]),
|
||||
octetstring_value(&data->fileData[i]),
|
||||
octetstring_length(&test_data.fileData[i])) == 0);
|
||||
octetstring_length(&test_data.fileData[i]) ==
|
||||
octetstring_length(&data->fileData[i]));
|
||||
ct_test(pTest,
|
||||
memcmp(octetstring_value(&test_data.fileData[i]),
|
||||
octetstring_value(&data->fileData[i]),
|
||||
octetstring_length(&test_data.fileData[i])) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicReadFileAck(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = {0};
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
|
||||
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
|
||||
unsigned int i = 0;
|
||||
|
||||
data.endOfFile = true;
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = 0;
|
||||
octetstring_init(&data.fileData[0], test_octet_string,
|
||||
sizeof(test_octet_string));
|
||||
octetstring_init(
|
||||
&data.fileData[0], test_octet_string, sizeof(test_octet_string));
|
||||
testAtomicReadFileAckAccess(pTest, &data);
|
||||
|
||||
data.endOfFile = false;
|
||||
@@ -410,8 +433,8 @@ void testAtomicReadFileAck(Test *pTest)
|
||||
data.type.record.fileStartRecord = 1;
|
||||
data.type.record.RecordCount = BACNET_READ_FILE_RECORD_COUNT;
|
||||
for (i = 0; i < data.type.record.RecordCount; i++) {
|
||||
octetstring_init(&data.fileData[i], test_octet_string,
|
||||
sizeof(test_octet_string));
|
||||
octetstring_init(
|
||||
&data.fileData[i], test_octet_string, sizeof(test_octet_string));
|
||||
}
|
||||
testAtomicReadFileAckAccess(pTest, &data);
|
||||
|
||||
@@ -420,8 +443,8 @@ void testAtomicReadFileAck(Test *pTest)
|
||||
|
||||
void testAtomicReadFileAccess(Test *pTest, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA test_data = {0};
|
||||
uint8_t apdu[480] = {0};
|
||||
BACNET_ATOMIC_READ_FILE_DATA test_data = { 0 };
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 128;
|
||||
@@ -437,21 +460,24 @@ void testAtomicReadFileAccess(Test *pTest, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
ct_test(pTest, test_data.object_instance == data->object_instance);
|
||||
ct_test(pTest, test_data.access == data->access);
|
||||
if (test_data.access == FILE_STREAM_ACCESS) {
|
||||
ct_test(pTest, test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest, test_data.type.stream.requestedOctetCount ==
|
||||
data->type.stream.requestedOctetCount);
|
||||
ct_test(pTest,
|
||||
test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest,
|
||||
test_data.type.stream.requestedOctetCount ==
|
||||
data->type.stream.requestedOctetCount);
|
||||
} else if (test_data.access == FILE_RECORD_ACCESS) {
|
||||
ct_test(pTest, test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest, test_data.type.record.RecordCount ==
|
||||
data->type.record.RecordCount);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.RecordCount == data->type.record.RecordCount);
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicReadFile(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = {0};
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = 1;
|
||||
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 ATOMIC_READ_FILE_H
|
||||
#define ATOMIC_READ_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
|
||||
#ifndef BACNET_READ_FILE_RECORD_COUNT
|
||||
#define BACNET_READ_FILE_RECORD_COUNT 1
|
||||
#endif
|
||||
|
||||
typedef struct BACnet_Atomic_Read_File_Data {
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
uint32_t object_instance;
|
||||
BACNET_FILE_ACCESS_METHOD access;
|
||||
union {
|
||||
struct {
|
||||
int32_t fileStartPosition;
|
||||
uint32_t requestedOctetCount;
|
||||
} stream;
|
||||
struct {
|
||||
int32_t fileStartRecord;
|
||||
/* requested or returned record count */
|
||||
uint32_t RecordCount;
|
||||
} record;
|
||||
} type;
|
||||
BACNET_OCTET_STRING fileData[BACNET_READ_FILE_RECORD_COUNT];
|
||||
bool endOfFile;
|
||||
} BACNET_ATOMIC_READ_FILE_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Atomic Read File */
|
||||
/* encode service */
|
||||
int arf_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
/* decode the service request only */
|
||||
int arf_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
int arf_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
/* Atomic Read File Ack */
|
||||
|
||||
/* encode service */
|
||||
int arf_ack_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
/* decode the service request only */
|
||||
int arf_ack_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
int arf_ack_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
|
||||
void test_AtomicReadFile(
|
||||
Test * pTest);
|
||||
void test_AtomicReadFileAck(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -22,34 +22,36 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "bacnet/assigned_access_rights.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
#include "assigned_access_rights.h"
|
||||
#include "bacdcode.h"
|
||||
|
||||
int bacapp_encode_assigned_access_rights(uint8_t* apdu,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS* aar)
|
||||
int bacapp_encode_assigned_access_rights(
|
||||
uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = bacapp_encode_context_device_obj_ref(&apdu[apdu_len], 0,
|
||||
&aar->assigned_access_rights);
|
||||
if (len < 0)
|
||||
len = bacapp_encode_context_device_obj_ref(
|
||||
&apdu[apdu_len], 0, &aar->assigned_access_rights);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_boolean(&apdu[apdu_len], 1, aar->enable);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_assigned_access_rights(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS* aar)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -66,8 +68,8 @@ int bacapp_encode_context_assigned_access_rights(
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_assigned_access_rights(uint8_t* apdu,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS* aar)
|
||||
int bacapp_decode_assigned_access_rights(
|
||||
uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -75,27 +77,31 @@ int bacapp_decode_assigned_access_rights(uint8_t* apdu,
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = bacapp_decode_context_device_obj_ref(
|
||||
&apdu[apdu_len], 0, &aar->assigned_access_rights);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_boolean2(&apdu[apdu_len], 1, &aar->enable);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_assigned_access_rights(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS* aar)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -0,0 +1,62 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACNET_ASSIGNED_ACCESS_RIGHTS_H
|
||||
#define BACNET_ASSIGNED_ACCESS_RIGHTS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
|
||||
typedef struct {
|
||||
BACNET_DEVICE_OBJECT_REFERENCE assigned_access_rights;
|
||||
bool enable;
|
||||
} BACNET_ASSIGNED_ACCESS_RIGHTS;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_assigned_access_rights(
|
||||
uint8_t * apdu,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS * aar);
|
||||
int bacapp_encode_context_assigned_access_rights(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS * aar);
|
||||
int bacapp_decode_assigned_access_rights(
|
||||
uint8_t * apdu,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS * aar);
|
||||
int bacapp_decode_context_assigned_access_rights(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag,
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS * aar);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -22,39 +22,42 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "bacnet/authentication_factor.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
#include "authentication_factor.h"
|
||||
#include "bacdcode.h"
|
||||
|
||||
int bacapp_encode_authentication_factor(uint8_t* apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR* af)
|
||||
int bacapp_encode_authentication_factor(
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 0, af->format_type);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 1, af->format_class);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_octet_string(&apdu[apdu_len], 2, &af->value);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_authentication_factor(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR* af)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -71,44 +74,50 @@ int bacapp_encode_context_authentication_factor(
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_authentication_factor(uint8_t* apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR* af)
|
||||
int bacapp_decode_authentication_factor(
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 0, &af->format_type);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 1, &af->format_class);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_octet_string(&apdu[apdu_len], 2, &af->value);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_authentication_factor(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR* af)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -0,0 +1,64 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACNET_AUTHENTICATION_FACTOR_H
|
||||
#define BACNET_AUTHENTICATION_FACTOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
|
||||
typedef struct {
|
||||
BACNET_AUTHENTICATION_FACTOR_TYPE format_type;
|
||||
uint32_t format_class;
|
||||
BACNET_OCTET_STRING value;
|
||||
} BACNET_AUTHENTICATION_FACTOR;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_authentication_factor(
|
||||
uint8_t * apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR * af);
|
||||
int bacapp_encode_context_authentication_factor(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag,
|
||||
BACNET_AUTHENTICATION_FACTOR * af);
|
||||
int bacapp_decode_authentication_factor(
|
||||
uint8_t * apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR * af);
|
||||
int bacapp_decode_context_authentication_factor(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag,
|
||||
BACNET_AUTHENTICATION_FACTOR * af);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
|
||||
@@ -22,40 +22,43 @@
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#include "bacdcode.h"
|
||||
#include "authentication_factor_format.h"
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/authentication_factor_format.h"
|
||||
|
||||
int bacapp_encode_authentication_factor_format(
|
||||
uint8_t* apdu, BACNET_AUTHENTICATION_FACTOR_FORMAT* aff)
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 0, aff->format_type);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
if (aff->format_type == AUTHENTICATION_FACTOR_CUSTOM) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 1, aff->vendor_id);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 2, aff->vendor_format);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
}
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_authentication_factor_format(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT* aff)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -73,47 +76,53 @@ int bacapp_encode_context_authentication_factor_format(
|
||||
}
|
||||
|
||||
int bacapp_decode_authentication_factor_format(
|
||||
uint8_t* apdu, BACNET_AUTHENTICATION_FACTOR_FORMAT* aff)
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 0, &aff->format_type);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
} else
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 1, &aff->vendor_id);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM) &&
|
||||
(aff->vendor_id != 0))
|
||||
(aff->vendor_id != 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 2, &aff->vendor_format);
|
||||
if (len < 0)
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
else
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM) &&
|
||||
(aff->vendor_format != 0))
|
||||
(aff->vendor_format != 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_authentication_factor_format(
|
||||
uint8_t* apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT* aff)
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -0,0 +1,60 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACNET_AUTHENTICATION_FACTOR_FORMAT_H
|
||||
#define BACNET_AUTHENTICATION_FACTOR_FORMAT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
typedef struct {
|
||||
BACNET_AUTHENTICATION_FACTOR_TYPE format_type;
|
||||
uint32_t vendor_id, vendor_format;
|
||||
} BACNET_AUTHENTICATION_FACTOR_FORMAT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_authentication_factor_format(
|
||||
uint8_t * apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff);
|
||||
int bacapp_encode_context_authentication_factor_format(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff);
|
||||
int bacapp_decode_authentication_factor_format(
|
||||
uint8_t * apdu,
|
||||
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff);
|
||||
int bacapp_decode_context_authentication_factor_format(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_AUTHENTICATION_FACTOR_FORMAT * aff);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+107
-80
@@ -32,16 +32,16 @@
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include "bacenum.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacdef.h"
|
||||
#include "awf.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/awf.h"
|
||||
|
||||
/** @file awf.c Atomic Write File */
|
||||
|
||||
/* encode service */
|
||||
int awf_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
uint32_t i = 0;
|
||||
@@ -59,8 +59,8 @@ int awf_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], 0);
|
||||
apdu_len += encode_application_signed(
|
||||
&apdu[apdu_len], data->type.stream.fileStartPosition);
|
||||
apdu_len += encode_application_octet_string(&apdu[apdu_len],
|
||||
&data->fileData[0]);
|
||||
apdu_len += encode_application_octet_string(
|
||||
&apdu[apdu_len], &data->fileData[0]);
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
@@ -84,8 +84,8 @@ int awf_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int awf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
@@ -101,8 +101,9 @@ int awf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
if (apdu_len && data) {
|
||||
len =
|
||||
decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_object_id(&apdu[len], &type, &data->object_instance);
|
||||
data->object_type = (BACNET_OBJECT_TYPE)type;
|
||||
if (decode_is_opening_tag_number(&apdu[len], 0)) {
|
||||
@@ -110,27 +111,30 @@ int awf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartPosition */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_signed(&apdu[len], len_value_type, &signed_value);
|
||||
data->type.stream.fileStartPosition = signed_value;
|
||||
/* fileData */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
|
||||
return -1;
|
||||
decoded_len = decode_octet_string(&apdu[len], len_value_type,
|
||||
&data->fileData[0]);
|
||||
}
|
||||
decoded_len = decode_octet_string(
|
||||
&apdu[len], len_value_type, &data->fileData[0]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 0))
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 0)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} else if (decode_is_opening_tag_number(&apdu[len], 1)) {
|
||||
@@ -138,61 +142,71 @@ int awf_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
/* fileStartRecord */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_SIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_signed(&apdu[len], len_value_type, &signed_value);
|
||||
data->type.record.fileStartRecord = signed_value;
|
||||
/* returnedRecordCount */
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) {
|
||||
return -1;
|
||||
}
|
||||
len += decode_unsigned(&apdu[len], len_value_type, &unsigned_value);
|
||||
data->type.record.returnedRecordCount = unsigned_value;
|
||||
/* fileData */
|
||||
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
|
||||
tag_len = decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
tag_len = decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
len += tag_len;
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING)
|
||||
if (tag_number != BACNET_APPLICATION_TAG_OCTET_STRING) {
|
||||
return -1;
|
||||
decoded_len = decode_octet_string(&apdu[len], len_value_type,
|
||||
&data->fileData[i]);
|
||||
}
|
||||
decoded_len = decode_octet_string(
|
||||
&apdu[len], len_value_type, &data->fileData[i]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
}
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1))
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int awf_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu)
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST)
|
||||
if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) {
|
||||
return -1;
|
||||
}
|
||||
/* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */
|
||||
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
|
||||
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE)
|
||||
if (apdu[3] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) {
|
||||
return -1;
|
||||
}
|
||||
offset = 4;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
@@ -203,8 +217,8 @@ int awf_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
return len;
|
||||
}
|
||||
|
||||
int awf_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_ack_encode_apdu(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
@@ -231,8 +245,8 @@ int awf_ack_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int awf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_ack_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
@@ -245,37 +259,43 @@ int awf_ack_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
if (tag_number == 0) {
|
||||
data->access = FILE_STREAM_ACCESS;
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.stream.fileStartPosition);
|
||||
&data->type.stream.fileStartPosition);
|
||||
} else if (tag_number == 1) {
|
||||
data->access = FILE_RECORD_ACCESS;
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&data->type.record.fileStartRecord);
|
||||
} else
|
||||
len += decode_signed(
|
||||
&apdu[len], len_value_type, &data->type.record.fileStartRecord);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int awf_ack_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
int awf_ack_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu)
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu[0] != PDU_TYPE_COMPLEX_ACK)
|
||||
if (apdu[0] != PDU_TYPE_COMPLEX_ACK) {
|
||||
return -1;
|
||||
}
|
||||
*invoke_id = apdu[1]; /* invoke id - filled in by net layer */
|
||||
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE)
|
||||
if (apdu[2] != SERVICE_CONFIRMED_ATOMIC_WRITE_FILE) {
|
||||
return -1;
|
||||
}
|
||||
offset = 3;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len = awf_ack_decode_service_request(&apdu[offset], apdu_len - offset,
|
||||
data);
|
||||
len = awf_ack_decode_service_request(
|
||||
&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -288,8 +308,8 @@ int awf_ack_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
|
||||
void testAtomicWriteFileAccess(Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA test_data = {0};
|
||||
uint8_t apdu[480] = {0};
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 };
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 128;
|
||||
@@ -305,32 +325,37 @@ void testAtomicWriteFileAccess(Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
ct_test(pTest, test_data.object_instance == data->object_instance);
|
||||
ct_test(pTest, test_data.access == data->access);
|
||||
if (test_data.access == FILE_STREAM_ACCESS) {
|
||||
ct_test(pTest, test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest,
|
||||
test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
} else if (test_data.access == FILE_RECORD_ACCESS) {
|
||||
ct_test(pTest, test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest, test_data.type.record.returnedRecordCount ==
|
||||
data->type.record.returnedRecordCount);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.returnedRecordCount ==
|
||||
data->type.record.returnedRecordCount);
|
||||
}
|
||||
ct_test(pTest, octetstring_length(&test_data.fileData[0]) ==
|
||||
octetstring_length(&data->fileData[0]));
|
||||
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData[0]),
|
||||
octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&test_data.fileData[0])) == 0);
|
||||
ct_test(pTest,
|
||||
octetstring_length(&test_data.fileData[0]) ==
|
||||
octetstring_length(&data->fileData[0]));
|
||||
ct_test(pTest,
|
||||
memcmp(octetstring_value(&test_data.fileData[0]),
|
||||
octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&test_data.fileData[0])) == 0);
|
||||
}
|
||||
|
||||
void testAtomicWriteFile(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data = {0};
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 };
|
||||
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = 1;
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = 0;
|
||||
octetstring_init(&data.fileData[0], test_octet_string,
|
||||
sizeof(test_octet_string));
|
||||
octetstring_init(
|
||||
&data.fileData[0], test_octet_string, sizeof(test_octet_string));
|
||||
testAtomicWriteFileAccess(pTest, &data);
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
@@ -338,18 +363,18 @@ void testAtomicWriteFile(Test *pTest)
|
||||
data.access = FILE_RECORD_ACCESS;
|
||||
data.type.record.fileStartRecord = 1;
|
||||
data.type.record.returnedRecordCount = 1;
|
||||
octetstring_init(&data.fileData[0], test_octet_string,
|
||||
sizeof(test_octet_string));
|
||||
octetstring_init(
|
||||
&data.fileData[0], test_octet_string, sizeof(test_octet_string));
|
||||
testAtomicWriteFileAccess(pTest, &data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void testAtomicWriteFileAckAccess(Test *pTest,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
void testAtomicWriteFileAckAccess(
|
||||
Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA test_data = {0};
|
||||
uint8_t apdu[480] = {0};
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA test_data = { 0 };
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 128;
|
||||
@@ -363,17 +388,19 @@ void testAtomicWriteFileAckAccess(Test *pTest,
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_data.access == data->access);
|
||||
if (test_data.access == FILE_STREAM_ACCESS) {
|
||||
ct_test(pTest, test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
ct_test(pTest,
|
||||
test_data.type.stream.fileStartPosition ==
|
||||
data->type.stream.fileStartPosition);
|
||||
} else if (test_data.access == FILE_RECORD_ACCESS) {
|
||||
ct_test(pTest, test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
ct_test(pTest,
|
||||
test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicWriteFileAck(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data = {0};
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 };
|
||||
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = 42;
|
||||
@@ -0,0 +1,105 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 ATOMIC_WRITE_FILE_H
|
||||
#define ATOMIC_WRITE_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
#ifndef BACNET_WRITE_FILE_RECORD_COUNT
|
||||
#define BACNET_WRITE_FILE_RECORD_COUNT 1
|
||||
#endif
|
||||
|
||||
typedef struct BACnet_Atomic_Write_File_Data {
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
uint32_t object_instance;
|
||||
BACNET_FILE_ACCESS_METHOD access;
|
||||
union {
|
||||
struct {
|
||||
int32_t fileStartPosition;
|
||||
} stream;
|
||||
struct {
|
||||
int32_t fileStartRecord;
|
||||
uint32_t returnedRecordCount;
|
||||
} record;
|
||||
} type;
|
||||
BACNET_OCTET_STRING fileData[BACNET_WRITE_FILE_RECORD_COUNT];
|
||||
} BACNET_ATOMIC_WRITE_FILE_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* Atomic Write File */
|
||||
/* encode service */
|
||||
int awf_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
/* decode the service request only */
|
||||
int awf_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
int awf_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
/* Atomic Write File Ack */
|
||||
/* encode service */
|
||||
int awf_ack_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
/* decode the service request only */
|
||||
int awf_ack_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
int awf_ack_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
|
||||
void test_AtomicWriteFile(
|
||||
Test * pTest);
|
||||
void test_AtomicWriteFileAck(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -34,13 +34,13 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacaddr.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacaddr.h"
|
||||
|
||||
/** @file bacaddr.c BACnet Address structure utilities */
|
||||
|
||||
void bacnet_address_copy(BACNET_ADDRESS* dest, BACNET_ADDRESS* src)
|
||||
void bacnet_address_copy(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
@@ -57,36 +57,44 @@ void bacnet_address_copy(BACNET_ADDRESS* dest, BACNET_ADDRESS* src)
|
||||
}
|
||||
}
|
||||
|
||||
bool bacnet_address_same(BACNET_ADDRESS* dest, BACNET_ADDRESS* src)
|
||||
bool bacnet_address_same(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
|
||||
{
|
||||
uint8_t i = 0; /* loop counter */
|
||||
uint8_t i = 0; /* loop counter */
|
||||
uint8_t max_len = 0; /* used for dynamic max */
|
||||
|
||||
if (dest == src) /* same ? */
|
||||
if (dest == src) { /* same ? */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dest->net != src->net)
|
||||
if (dest->net != src->net) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest->len != src->len)
|
||||
if (dest->len != src->len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
max_len = dest->len;
|
||||
if (max_len > MAX_MAC_LEN)
|
||||
if (max_len > MAX_MAC_LEN) {
|
||||
max_len = MAX_MAC_LEN;
|
||||
}
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (dest->adr[i] != src->adr[i])
|
||||
if (dest->adr[i] != src->adr[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (dest->net == 0) {
|
||||
if (dest->mac_len != src->mac_len)
|
||||
if (dest->mac_len != src->mac_len) {
|
||||
return false;
|
||||
}
|
||||
max_len = dest->mac_len;
|
||||
if (max_len > MAX_MAC_LEN)
|
||||
if (max_len > MAX_MAC_LEN) {
|
||||
max_len = MAX_MAC_LEN;
|
||||
}
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (dest->mac[i] != src->mac[i])
|
||||
if (dest->mac[i] != src->mac[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACADDR_H
|
||||
#define BACADDR_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bacnet_address_copy(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
bool bacnet_address_same(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+260
-272
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,256 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACAPP_H
|
||||
#define BACAPP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#if defined (BACAPP_LIGHTING_COMMAND)
|
||||
#include "bacnet/lighting.h"
|
||||
#endif
|
||||
#if defined (BACAPP_DEVICE_OBJECT_PROP_REF)
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#endif
|
||||
|
||||
|
||||
struct BACnet_Application_Data_Value;
|
||||
typedef struct BACnet_Application_Data_Value {
|
||||
bool context_specific; /* true if context specific data */
|
||||
uint8_t context_tag; /* only used for context specific data */
|
||||
uint8_t tag; /* application tag data type */
|
||||
union {
|
||||
/* NULL - not needed as it is encoded in the tag alone */
|
||||
#if defined (BACAPP_BOOLEAN)
|
||||
bool Boolean;
|
||||
#endif
|
||||
#if defined (BACAPP_UNSIGNED)
|
||||
uint32_t Unsigned_Int;
|
||||
#endif
|
||||
#if defined (BACAPP_SIGNED)
|
||||
int32_t Signed_Int;
|
||||
#endif
|
||||
#if defined (BACAPP_REAL)
|
||||
float Real;
|
||||
#endif
|
||||
#if defined (BACAPP_DOUBLE)
|
||||
double Double;
|
||||
#endif
|
||||
#if defined (BACAPP_OCTET_STRING)
|
||||
BACNET_OCTET_STRING Octet_String;
|
||||
#endif
|
||||
#if defined (BACAPP_CHARACTER_STRING)
|
||||
BACNET_CHARACTER_STRING Character_String;
|
||||
#endif
|
||||
#if defined (BACAPP_BIT_STRING)
|
||||
BACNET_BIT_STRING Bit_String;
|
||||
#endif
|
||||
#if defined (BACAPP_ENUMERATED)
|
||||
uint32_t Enumerated;
|
||||
#endif
|
||||
#if defined (BACAPP_DATE)
|
||||
BACNET_DATE Date;
|
||||
#endif
|
||||
#if defined (BACAPP_TIME)
|
||||
BACNET_TIME Time;
|
||||
#endif
|
||||
#if defined (BACAPP_OBJECT_ID)
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
#endif
|
||||
#if defined (BACAPP_LIGHTING_COMMAND)
|
||||
BACNET_LIGHTING_COMMAND Lighting_Command;
|
||||
#endif
|
||||
#if defined (BACAPP_DEVICE_OBJECT_PROP_REF)
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE
|
||||
Device_Object_Property_Reference;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
struct BACnet_Application_Data_Value *next;
|
||||
} BACNET_APPLICATION_DATA_VALUE;
|
||||
|
||||
struct BACnet_Access_Error;
|
||||
typedef struct BACnet_Access_Error {
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
} BACNET_ACCESS_ERROR;
|
||||
|
||||
struct BACnet_Property_Reference;
|
||||
typedef struct BACnet_Property_Reference {
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t propertyArrayIndex; /* optional */
|
||||
/* either value or error, but not both.
|
||||
Use NULL value to indicate error */
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
BACNET_ACCESS_ERROR error;
|
||||
/* simple linked list */
|
||||
struct BACnet_Property_Reference *next;
|
||||
} BACNET_PROPERTY_REFERENCE;
|
||||
|
||||
struct BACnet_Property_Value;
|
||||
typedef struct BACnet_Property_Value {
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t propertyArrayIndex;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
uint8_t priority;
|
||||
/* simple linked list */
|
||||
struct BACnet_Property_Value *next;
|
||||
} BACNET_PROPERTY_VALUE;
|
||||
|
||||
/* used for printing values */
|
||||
struct BACnet_Object_Property_Value;
|
||||
typedef struct BACnet_Object_Property_Value {
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
uint32_t object_instance;
|
||||
BACNET_PROPERTY_ID object_property;
|
||||
uint32_t array_index;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
} BACNET_OBJECT_PROPERTY_VALUE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bacapp_value_list_init(
|
||||
BACNET_APPLICATION_DATA_VALUE *value,
|
||||
size_t count);
|
||||
void bacapp_property_value_list_init(
|
||||
BACNET_PROPERTY_VALUE *value,
|
||||
size_t count);
|
||||
|
||||
int bacapp_encode_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
int bacapp_decode_data(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_data_type,
|
||||
uint32_t len_value_type,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_decode_application_data(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
bool bacapp_decode_application_data_safe(
|
||||
uint8_t * new_apdu,
|
||||
uint32_t new_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_encode_application_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_decode_context_data(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
int bacapp_encode_context_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
int bacapp_encode_context_data_value(
|
||||
uint8_t * apdu,
|
||||
uint8_t context_tag_number,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
BACNET_APPLICATION_TAG bacapp_context_tag_type(
|
||||
BACNET_PROPERTY_ID property,
|
||||
uint8_t tag_number);
|
||||
|
||||
bool bacapp_copy(
|
||||
BACNET_APPLICATION_DATA_VALUE * dest_value,
|
||||
BACNET_APPLICATION_DATA_VALUE * src_value);
|
||||
|
||||
/* returns the length of data between an opening tag and a closing tag.
|
||||
Expects that the first octet contain the opening tag.
|
||||
Include a value property identifier for context specific data
|
||||
such as the value received in a WriteProperty request */
|
||||
int bacapp_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_PROPERTY_ID property);
|
||||
int bacapp_decode_data_len(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_data_type,
|
||||
uint32_t len_value_type);
|
||||
int bacapp_decode_application_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len);
|
||||
int bacapp_decode_context_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
#ifndef BACAPP_PRINT_ENABLED
|
||||
#if PRINT_ENABLED || defined TEST
|
||||
#define BACAPP_PRINT_ENABLED
|
||||
#define BACAPP_SNPRINTF_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BACAPP_SNPRINTF_ENABLED
|
||||
int bacapp_snprintf_value(
|
||||
char *str,
|
||||
size_t str_len,
|
||||
BACNET_OBJECT_PROPERTY_VALUE * object_value);
|
||||
#endif
|
||||
|
||||
#ifdef BACAPP_PRINT_ENABLED
|
||||
bool bacapp_parse_application_data(
|
||||
BACNET_APPLICATION_TAG tag_number,
|
||||
const char *argv,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
bool bacapp_print_value(
|
||||
FILE * stream,
|
||||
BACNET_OBJECT_PROPERTY_VALUE * value);
|
||||
#else
|
||||
/* Provide harmless return values */
|
||||
#define bacapp_parse_application_data(x,y,z) false
|
||||
#define bacapp_print_value(x,y) false
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
#include "bacnet/datetime.h"
|
||||
bool bacapp_same_value(
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_APPLICATION_DATA_VALUE * test_value);
|
||||
|
||||
void testBACnetApplicationDataLength(
|
||||
Test * pTest);
|
||||
void testBACnetApplicationData(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -34,13 +34,13 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "bits.h"
|
||||
#include "bacstr.h"
|
||||
#include "bacint.h"
|
||||
#include "bacreal.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacreal.h"
|
||||
|
||||
/** @file bacdcode.c Functions to encode/decode BACnet data types */
|
||||
|
||||
@@ -97,39 +97,41 @@ uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu)
|
||||
{
|
||||
uint8_t octet = 0;
|
||||
|
||||
if (max_segs < 2)
|
||||
if (max_segs < 2) {
|
||||
octet = 0;
|
||||
else if (max_segs < 4)
|
||||
} else if (max_segs < 4) {
|
||||
octet = 0x10;
|
||||
else if (max_segs < 8)
|
||||
} else if (max_segs < 8) {
|
||||
octet = 0x20;
|
||||
else if (max_segs < 16)
|
||||
} else if (max_segs < 16) {
|
||||
octet = 0x30;
|
||||
else if (max_segs < 32)
|
||||
} else if (max_segs < 32) {
|
||||
octet = 0x40;
|
||||
else if (max_segs < 64)
|
||||
} else if (max_segs < 64) {
|
||||
octet = 0x50;
|
||||
else if (max_segs == 64)
|
||||
} else if (max_segs == 64) {
|
||||
octet = 0x60;
|
||||
else
|
||||
} else {
|
||||
octet = 0x70;
|
||||
}
|
||||
|
||||
/* max_apdu must be 50 octets minimum */
|
||||
if (max_apdu <= 50)
|
||||
if (max_apdu <= 50) {
|
||||
octet |= 0x00;
|
||||
else if (max_apdu <= 128)
|
||||
} else if (max_apdu <= 128) {
|
||||
octet |= 0x01;
|
||||
/*fits in a LonTalk frame */
|
||||
else if (max_apdu <= 206)
|
||||
/*fits in a LonTalk frame */
|
||||
} else if (max_apdu <= 206) {
|
||||
octet |= 0x02;
|
||||
/*fits in an ARCNET or MS/TP frame */
|
||||
else if (max_apdu <= 480)
|
||||
/*fits in an ARCNET or MS/TP frame */
|
||||
} else if (max_apdu <= 480) {
|
||||
octet |= 0x03;
|
||||
else if (max_apdu <= 1024)
|
||||
} else if (max_apdu <= 1024) {
|
||||
octet |= 0x04;
|
||||
/* fits in an ISO 8802-3 frame */
|
||||
else if (max_apdu <= 1476)
|
||||
/* fits in an ISO 8802-3 frame */
|
||||
} else if (max_apdu <= 1476) {
|
||||
octet |= 0x05;
|
||||
}
|
||||
|
||||
return octet;
|
||||
}
|
||||
@@ -205,14 +207,17 @@ int decode_max_apdu(uint8_t octet)
|
||||
|
||||
/* from clause 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_tag(uint8_t *apdu, uint8_t tag_number, bool context_specific,
|
||||
uint32_t len_value_type)
|
||||
int encode_tag(uint8_t *apdu,
|
||||
uint8_t tag_number,
|
||||
bool context_specific,
|
||||
uint32_t len_value_type)
|
||||
{
|
||||
int len = 1; /* return value */
|
||||
|
||||
apdu[0] = 0;
|
||||
if (context_specific)
|
||||
if (context_specific) {
|
||||
apdu[0] = BIT3;
|
||||
}
|
||||
|
||||
/* additional tag byte after this byte */
|
||||
/* for extended tag byte */
|
||||
@@ -309,8 +314,8 @@ int decode_tag_number(uint8_t *apdu, uint8_t *tag_number)
|
||||
}
|
||||
|
||||
/* Same as function above, but will safely fail if packet has been truncated */
|
||||
int decode_tag_number_safe(uint8_t *apdu, uint32_t apdu_len_remaining,
|
||||
uint8_t *tag_number)
|
||||
int decode_tag_number_safe(
|
||||
uint8_t *apdu, uint32_t apdu_len_remaining, uint8_t *tag_number)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
|
||||
@@ -346,8 +351,8 @@ bool decode_is_closing_tag(uint8_t *apdu)
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_tag_number_and_value(uint8_t *apdu, uint8_t *tag_number,
|
||||
uint32_t *value)
|
||||
int decode_tag_number_and_value(
|
||||
uint8_t *apdu, uint8_t *tag_number, uint32_t *value)
|
||||
{
|
||||
int len = 1;
|
||||
uint16_t value16;
|
||||
@@ -392,8 +397,10 @@ int decode_tag_number_and_value(uint8_t *apdu, uint8_t *tag_number,
|
||||
}
|
||||
|
||||
/* Same as function above, but will safely fail is packet has been truncated */
|
||||
int decode_tag_number_and_value_safe(uint8_t *apdu, uint32_t apdu_len_remaining,
|
||||
uint8_t *tag_number, uint32_t *value)
|
||||
int decode_tag_number_and_value_safe(uint8_t *apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
uint8_t *tag_number,
|
||||
uint32_t *value)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -453,8 +460,8 @@ bool decode_is_context_tag(uint8_t *apdu, uint8_t tag_number)
|
||||
return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number));
|
||||
}
|
||||
|
||||
bool decode_is_context_tag_with_length(uint8_t *apdu, uint8_t tag_number,
|
||||
int *tag_length)
|
||||
bool decode_is_context_tag_with_length(
|
||||
uint8_t *apdu, uint8_t tag_number, int *tag_length)
|
||||
{
|
||||
uint8_t my_tag_number = 0;
|
||||
|
||||
@@ -501,8 +508,8 @@ int encode_application_boolean(uint8_t *apdu, bool boolean_value)
|
||||
}
|
||||
|
||||
/* context tagged is encoded differently */
|
||||
int encode_context_boolean(uint8_t *apdu, uint8_t tag_number,
|
||||
bool boolean_value)
|
||||
int encode_context_boolean(
|
||||
uint8_t *apdu, uint8_t tag_number, bool boolean_value)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
|
||||
@@ -524,8 +531,8 @@ bool decode_context_boolean(uint8_t *apdu)
|
||||
return boolean_value;
|
||||
}
|
||||
|
||||
int decode_context_boolean2(uint8_t *apdu, uint8_t tag_number,
|
||||
bool *boolean_value)
|
||||
int decode_context_boolean2(
|
||||
uint8_t *apdu, uint8_t tag_number, bool *boolean_value)
|
||||
{
|
||||
int len = 0;
|
||||
if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) {
|
||||
@@ -602,8 +609,8 @@ static uint8_t byte_reverse_bits(uint8_t in_byte)
|
||||
|
||||
/* from clause 20.2.10 Encoding of a Bit String Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_bitstring(uint8_t *apdu, uint32_t len_value,
|
||||
BACNET_BIT_STRING *bit_string)
|
||||
int decode_bitstring(
|
||||
uint8_t *apdu, uint32_t len_value, BACNET_BIT_STRING *bit_string)
|
||||
{
|
||||
int len = 0;
|
||||
uint8_t unused_bits = 0;
|
||||
@@ -617,20 +624,20 @@ int decode_bitstring(uint8_t *apdu, uint32_t len_value,
|
||||
if (bytes_used <= MAX_BITSTRING_BYTES) {
|
||||
len = 1;
|
||||
for (i = 0; i < bytes_used; i++) {
|
||||
bitstring_set_octet(bit_string, (uint8_t)i,
|
||||
byte_reverse_bits(apdu[len++]));
|
||||
bitstring_set_octet(
|
||||
bit_string, (uint8_t)i, byte_reverse_bits(apdu[len++]));
|
||||
}
|
||||
unused_bits = (uint8_t)(apdu[0] & 0x07);
|
||||
bitstring_set_bits_used(bit_string, (uint8_t)bytes_used,
|
||||
unused_bits);
|
||||
bitstring_set_bits_used(
|
||||
bit_string, (uint8_t)bytes_used, unused_bits);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int decode_context_bitstring(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_BIT_STRING *bit_string)
|
||||
int decode_context_bitstring(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_BIT_STRING *bit_string)
|
||||
{
|
||||
uint32_t len_value;
|
||||
int len = 0;
|
||||
@@ -679,14 +686,14 @@ int encode_application_bitstring(uint8_t *apdu, BACNET_BIT_STRING *bit_string)
|
||||
/* bit string may use more than 1 octet for the tag, so find out how many */
|
||||
bit_string_encoded_length += bitstring_bytes_used(bit_string);
|
||||
len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BIT_STRING, false,
|
||||
bit_string_encoded_length);
|
||||
bit_string_encoded_length);
|
||||
len += encode_bitstring(&apdu[len], bit_string);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int encode_context_bitstring(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_BIT_STRING *bit_string)
|
||||
int encode_context_bitstring(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_BIT_STRING *bit_string)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t bit_string_encoded_length = 1; /* 1 for the bits remaining octet */
|
||||
@@ -714,8 +721,10 @@ int decode_object_id(uint8_t *apdu, uint16_t *object_type, uint32_t *instance)
|
||||
return len;
|
||||
}
|
||||
|
||||
int decode_object_id_safe(uint8_t *apdu, uint32_t len_value,
|
||||
uint16_t *object_type, uint32_t *instance)
|
||||
int decode_object_id_safe(uint8_t *apdu,
|
||||
uint32_t len_value,
|
||||
uint16_t *object_type,
|
||||
uint32_t *instance)
|
||||
{
|
||||
if (len_value != 4) {
|
||||
return 0;
|
||||
@@ -724,8 +733,10 @@ int decode_object_id_safe(uint8_t *apdu, uint32_t len_value,
|
||||
}
|
||||
}
|
||||
|
||||
int decode_context_object_id(uint8_t *apdu, uint8_t tag_number,
|
||||
uint16_t *object_type, uint32_t *instance)
|
||||
int decode_context_object_id(uint8_t *apdu,
|
||||
uint8_t tag_number,
|
||||
uint16_t *object_type,
|
||||
uint32_t *instance)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -748,7 +759,7 @@ int encode_bacnet_object_id(uint8_t *apdu, int object_type, uint32_t instance)
|
||||
|
||||
type = (uint32_t)object_type;
|
||||
value = ((type & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) |
|
||||
(instance & BACNET_MAX_INSTANCE);
|
||||
(instance & BACNET_MAX_INSTANCE);
|
||||
len = encode_unsigned32(apdu, value);
|
||||
|
||||
return len;
|
||||
@@ -757,8 +768,8 @@ int encode_bacnet_object_id(uint8_t *apdu, int object_type, uint32_t instance)
|
||||
/* from clause 20.2.14 Encoding of an Object Identifier Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_context_object_id(uint8_t *apdu, uint8_t tag_number, int object_type,
|
||||
uint32_t instance)
|
||||
int encode_context_object_id(
|
||||
uint8_t *apdu, uint8_t tag_number, int object_type, uint32_t instance)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -773,15 +784,15 @@ int encode_context_object_id(uint8_t *apdu, uint8_t tag_number, int object_type,
|
||||
/* from clause 20.2.14 Encoding of an Object Identifier Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_object_id(uint8_t *apdu, int object_type,
|
||||
uint32_t instance)
|
||||
int encode_application_object_id(
|
||||
uint8_t *apdu, int object_type, uint32_t instance)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
/* assumes that the tag only consumes 1 octet */
|
||||
len = encode_bacnet_object_id(&apdu[1], object_type, instance);
|
||||
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OBJECT_ID, false,
|
||||
(uint32_t)len);
|
||||
len += encode_tag(
|
||||
&apdu[0], BACNET_APPLICATION_TAG_OBJECT_ID, false, (uint32_t)len);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -814,14 +825,14 @@ int encode_octet_string(uint8_t *apdu, BACNET_OCTET_STRING *octet_string)
|
||||
/* from clause 20.2.8 Encoding of an Octet String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_octet_string(uint8_t *apdu,
|
||||
BACNET_OCTET_STRING *octet_string)
|
||||
int encode_application_octet_string(
|
||||
uint8_t *apdu, BACNET_OCTET_STRING *octet_string)
|
||||
{
|
||||
int apdu_len = 0;
|
||||
|
||||
if (octet_string) {
|
||||
apdu_len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OCTET_STRING,
|
||||
false, octetstring_length(octet_string));
|
||||
false, octetstring_length(octet_string));
|
||||
/* FIXME: probably need to pass in the length of the APDU
|
||||
to bounds check since it might not be the only data chunk */
|
||||
if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) {
|
||||
@@ -837,14 +848,14 @@ int encode_application_octet_string(uint8_t *apdu,
|
||||
/* from clause 20.2.8 Encoding of an Octet String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_context_octet_string(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_OCTET_STRING *octet_string)
|
||||
int encode_context_octet_string(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_OCTET_STRING *octet_string)
|
||||
{
|
||||
int apdu_len = 0;
|
||||
|
||||
if (apdu && octet_string) {
|
||||
apdu_len = encode_tag(&apdu[0], tag_number, true,
|
||||
octetstring_length(octet_string));
|
||||
apdu_len = encode_tag(
|
||||
&apdu[0], tag_number, true, octetstring_length(octet_string));
|
||||
if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) {
|
||||
apdu_len += encode_octet_string(&apdu[apdu_len], octet_string);
|
||||
} else {
|
||||
@@ -858,8 +869,8 @@ int encode_context_octet_string(uint8_t *apdu, uint8_t tag_number,
|
||||
/* from clause 20.2.8 Encoding of an Octet String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_octet_string(uint8_t *apdu, uint32_t len_value,
|
||||
BACNET_OCTET_STRING *octet_string)
|
||||
int decode_octet_string(
|
||||
uint8_t *apdu, uint32_t len_value, BACNET_OCTET_STRING *octet_string)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
bool status = false;
|
||||
@@ -872,8 +883,8 @@ int decode_octet_string(uint8_t *apdu, uint32_t len_value,
|
||||
return len;
|
||||
}
|
||||
|
||||
int decode_context_octet_string(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_OCTET_STRING *octet_string)
|
||||
int decode_context_octet_string(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_OCTET_STRING *octet_string)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
bool status = false;
|
||||
@@ -898,9 +909,11 @@ int decode_context_octet_string(uint8_t *apdu, uint8_t tag_number,
|
||||
|
||||
/* from clause 20.2.9 Encoding of a Character String Value */
|
||||
/* returns the number of apdu bytes consumed, or zero if failed */
|
||||
uint32_t encode_bacnet_character_string_safe(uint8_t *apdu, uint32_t max_apdu,
|
||||
uint8_t encoding, char *pString,
|
||||
uint32_t length)
|
||||
uint32_t encode_bacnet_character_string_safe(uint8_t *apdu,
|
||||
uint32_t max_apdu,
|
||||
uint8_t encoding,
|
||||
char *pString,
|
||||
uint32_t length)
|
||||
{
|
||||
uint32_t apdu_len = 1 /*encoding */;
|
||||
uint32_t i;
|
||||
@@ -918,11 +931,11 @@ uint32_t encode_bacnet_character_string_safe(uint8_t *apdu, uint32_t max_apdu,
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int encode_bacnet_character_string(uint8_t *apdu,
|
||||
BACNET_CHARACTER_STRING *char_string)
|
||||
int encode_bacnet_character_string(
|
||||
uint8_t *apdu, BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
return (int)encode_bacnet_character_string_safe(
|
||||
apdu, MAX_APDU, characterstring_encoding(char_string),
|
||||
return (int)encode_bacnet_character_string_safe(apdu, MAX_APDU,
|
||||
characterstring_encoding(char_string),
|
||||
characterstring_value(char_string),
|
||||
characterstring_length(char_string));
|
||||
}
|
||||
@@ -930,8 +943,8 @@ int encode_bacnet_character_string(uint8_t *apdu,
|
||||
/* from clause 20.2.9 Encoding of a Character String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_character_string(uint8_t *apdu,
|
||||
BACNET_CHARACTER_STRING *char_string)
|
||||
int encode_application_character_string(
|
||||
uint8_t *apdu, BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
int len = 0;
|
||||
int string_len = 0;
|
||||
@@ -939,7 +952,7 @@ int encode_application_character_string(uint8_t *apdu,
|
||||
string_len =
|
||||
(int)characterstring_length(char_string) + 1 /* for encoding */;
|
||||
len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_CHARACTER_STRING, false,
|
||||
(uint32_t)string_len);
|
||||
(uint32_t)string_len);
|
||||
if ((len + string_len) < MAX_APDU) {
|
||||
len += encode_bacnet_character_string(&apdu[len], char_string);
|
||||
} else {
|
||||
@@ -949,8 +962,8 @@ int encode_application_character_string(uint8_t *apdu,
|
||||
return len;
|
||||
}
|
||||
|
||||
int encode_context_character_string(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING *char_string)
|
||||
int encode_context_character_string(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
int len = 0;
|
||||
int string_len = 0;
|
||||
@@ -970,14 +983,14 @@ int encode_context_character_string(uint8_t *apdu, uint8_t tag_number,
|
||||
/* from clause 20.2.9 Encoding of a Character String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_character_string(uint8_t *apdu, uint32_t len_value,
|
||||
BACNET_CHARACTER_STRING *char_string)
|
||||
int decode_character_string(
|
||||
uint8_t *apdu, uint32_t len_value, BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
bool status = false;
|
||||
|
||||
status = characterstring_init(char_string, apdu[0], (char *)&apdu[1],
|
||||
len_value - 1);
|
||||
status = characterstring_init(
|
||||
char_string, apdu[0], (char *)&apdu[1], len_value - 1);
|
||||
if (status) {
|
||||
len = (int)len_value;
|
||||
}
|
||||
@@ -985,8 +998,8 @@ int decode_character_string(uint8_t *apdu, uint32_t len_value,
|
||||
return len;
|
||||
}
|
||||
|
||||
int decode_context_character_string(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING *char_string)
|
||||
int decode_context_character_string(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
bool status = false;
|
||||
@@ -996,8 +1009,8 @@ int decode_context_character_string(uint8_t *apdu, uint8_t tag_number,
|
||||
!decode_is_closing_tag(&apdu[len])) {
|
||||
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
|
||||
|
||||
status = characterstring_init(char_string, apdu[len],
|
||||
(char *)&apdu[len + 1], len_value - 1);
|
||||
status = characterstring_init(
|
||||
char_string, apdu[len], (char *)&apdu[len + 1], len_value - 1);
|
||||
if (status) {
|
||||
len += len_value;
|
||||
}
|
||||
@@ -1107,8 +1120,8 @@ int encode_application_unsigned(uint8_t *apdu, uint32_t value)
|
||||
int len = 0;
|
||||
|
||||
len = encode_bacnet_unsigned(&apdu[1], value);
|
||||
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_UNSIGNED_INT, false,
|
||||
(uint32_t)len);
|
||||
len += encode_tag(
|
||||
&apdu[0], BACNET_APPLICATION_TAG_UNSIGNED_INT, false, (uint32_t)len);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -1162,8 +1175,8 @@ int encode_application_enumerated(uint8_t *apdu, uint32_t value)
|
||||
|
||||
/* assumes that the tag only consumes 1 octet */
|
||||
len = encode_bacnet_enumerated(&apdu[1], value);
|
||||
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_ENUMERATED, false,
|
||||
(uint32_t)len);
|
||||
len += encode_tag(
|
||||
&apdu[0], BACNET_APPLICATION_TAG_ENUMERATED, false, (uint32_t)len);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -1270,8 +1283,8 @@ int encode_application_signed(uint8_t *apdu, int32_t value)
|
||||
|
||||
/* assumes that the tag only consumes 1 octet */
|
||||
len = encode_bacnet_signed(&apdu[1], value);
|
||||
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_SIGNED_INT, false,
|
||||
(uint32_t)len);
|
||||
len += encode_tag(
|
||||
&apdu[0], BACNET_APPLICATION_TAG_SIGNED_INT, false, (uint32_t)len);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -1337,8 +1350,8 @@ int encode_application_double(uint8_t *apdu, double value)
|
||||
/* assumes that the tag only consumes 2 octet */
|
||||
len = encode_bacnet_double(value, &apdu[2]);
|
||||
|
||||
len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_DOUBLE, false,
|
||||
(uint32_t)len);
|
||||
len += encode_tag(
|
||||
&apdu[0], BACNET_APPLICATION_TAG_DOUBLE, false, (uint32_t)len);
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -1407,8 +1420,8 @@ int decode_bacnet_time(uint8_t *apdu, BACNET_TIME *btime)
|
||||
return 4;
|
||||
}
|
||||
|
||||
int decode_bacnet_time_safe(uint8_t *apdu, uint32_t len_value,
|
||||
BACNET_TIME *btime)
|
||||
int decode_bacnet_time_safe(
|
||||
uint8_t *apdu, uint32_t len_value, BACNET_TIME *btime)
|
||||
{
|
||||
if (len_value != 4) {
|
||||
btime->hour = 0;
|
||||
@@ -1437,8 +1450,8 @@ int decode_application_time(uint8_t *apdu, BACNET_TIME *btime)
|
||||
return len;
|
||||
}
|
||||
|
||||
int decode_context_bacnet_time(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_TIME *btime)
|
||||
int decode_context_bacnet_time(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_TIME *btime)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -1578,10 +1591,11 @@ int encode_bacnet_address(uint8_t *apdu, BACNET_ADDRESS *destination)
|
||||
/* network number */
|
||||
apdu_len += encode_application_unsigned(&apdu[apdu_len], destination->net);
|
||||
/* encode mac address as an octet-string */
|
||||
if (destination->len != 0)
|
||||
if (destination->len != 0) {
|
||||
octetstring_init(&mac_addr, destination->adr, destination->len);
|
||||
else
|
||||
} else {
|
||||
octetstring_init(&mac_addr, destination->mac, destination->mac_len);
|
||||
}
|
||||
apdu_len += encode_application_octet_string(&apdu[apdu_len], &mac_addr);
|
||||
return apdu_len;
|
||||
}
|
||||
@@ -1595,7 +1609,7 @@ int decode_bacnet_address(uint8_t *apdu, BACNET_ADDRESS *destination)
|
||||
uint8_t i = 0;
|
||||
uint32_t data_unsigned = 0;
|
||||
uint8_t tag_number = 0;
|
||||
BACNET_OCTET_STRING mac_addr = {0};
|
||||
BACNET_OCTET_STRING mac_addr = { 0 };
|
||||
|
||||
/* network number */
|
||||
tag_len =
|
||||
@@ -1628,8 +1642,8 @@ int decode_bacnet_address(uint8_t *apdu, BACNET_ADDRESS *destination)
|
||||
}
|
||||
|
||||
/* BACnetAddress */
|
||||
int encode_context_bacnet_address(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_ADDRESS *destination)
|
||||
int encode_context_bacnet_address(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ADDRESS *destination)
|
||||
{
|
||||
int apdu_len = 0;
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], tag_number);
|
||||
@@ -1639,8 +1653,8 @@ int encode_context_bacnet_address(uint8_t *apdu, uint8_t tag_number,
|
||||
}
|
||||
|
||||
/* BACnetAddress */
|
||||
int decode_context_bacnet_address(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_ADDRESS *destination)
|
||||
int decode_context_bacnet_address(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ADDRESS *destination)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -1693,11 +1707,11 @@ static int get_apdu_len(bool extended_tag, uint32_t value)
|
||||
|
||||
static void print_apdu(uint8_t *pBlock, uint32_t num)
|
||||
{
|
||||
size_t lines = 0; /* number of lines to print */
|
||||
size_t line = 0; /* line of text counter */
|
||||
size_t last_line = 0; /* line on which the last text resided */
|
||||
size_t lines = 0; /* number of lines to print */
|
||||
size_t line = 0; /* line of text counter */
|
||||
size_t last_line = 0; /* line on which the last text resided */
|
||||
unsigned long count = 0; /* address to print */
|
||||
unsigned int i = 0; /* counter */
|
||||
unsigned int i = 0; /* counter */
|
||||
|
||||
if (pBlock && num) {
|
||||
/* how many lines to print? */
|
||||
@@ -1743,7 +1757,7 @@ static void print_apdu(uint8_t *pBlock, uint32_t num)
|
||||
|
||||
static void testBACDCodeTags(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint8_t tag_number = 0, test_tag_number = 0;
|
||||
int len = 0, test_len = 0;
|
||||
uint32_t value = 0, test_value = 0;
|
||||
@@ -1769,8 +1783,8 @@ static void testBACDCodeTags(Test *pTest)
|
||||
/* test the len-value-type portion */
|
||||
for (value = 1;; value = value << 1) {
|
||||
len = encode_tag(&apdu[0], tag_number, false, value);
|
||||
len = decode_tag_number_and_value(&apdu[0], &test_tag_number,
|
||||
&test_value);
|
||||
len = decode_tag_number_and_value(
|
||||
&apdu[0], &test_tag_number, &test_value);
|
||||
ct_test(pTest, tag_number == test_tag_number);
|
||||
ct_test(pTest, value == test_value);
|
||||
test_len = get_apdu_len(IS_EXTENDED_TAG_NUMBER(apdu[0]), value);
|
||||
@@ -1791,13 +1805,13 @@ static void testBACDCodeTags(Test *pTest)
|
||||
|
||||
static void testBACDCodeEnumerated(Test *pTest)
|
||||
{
|
||||
uint8_t array[5] = {0};
|
||||
uint8_t encoded_array[5] = {0};
|
||||
uint8_t array[5] = { 0 };
|
||||
uint8_t encoded_array[5] = { 0 };
|
||||
uint32_t value = 1;
|
||||
uint32_t decoded_value = 0;
|
||||
int i = 0, apdu_len = 0;
|
||||
int len = 0;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
|
||||
@@ -1810,8 +1824,8 @@ static void testBACDCodeEnumerated(Test *pTest)
|
||||
ct_test(pTest, len == apdu_len);
|
||||
/* encode back the value */
|
||||
encode_application_enumerated(&encoded_array[0], decoded_value);
|
||||
ct_test(pTest,
|
||||
memcmp(&array[0], &encoded_array[0], sizeof(array)) == 0);
|
||||
ct_test(
|
||||
pTest, memcmp(&array[0], &encoded_array[0], sizeof(array)) == 0);
|
||||
/* an enumerated will take up to 4 octects */
|
||||
/* plus a one octet for the tag */
|
||||
apdu_len = encode_application_enumerated(&apdu[0], value);
|
||||
@@ -1834,11 +1848,11 @@ static void testBACDCodeEnumerated(Test *pTest)
|
||||
|
||||
static void testBACDCodeReal(Test *pTest)
|
||||
{
|
||||
uint8_t real_array[4] = {0};
|
||||
uint8_t encoded_array[4] = {0};
|
||||
uint8_t real_array[4] = { 0 };
|
||||
uint8_t encoded_array[4] = { 0 };
|
||||
float value = 42.123F;
|
||||
float decoded_value = 0.0F;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, apdu_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t long_value = 0;
|
||||
@@ -1847,8 +1861,8 @@ static void testBACDCodeReal(Test *pTest)
|
||||
decode_real(&real_array[0], &decoded_value);
|
||||
ct_test(pTest, decoded_value == value);
|
||||
encode_bacnet_real(value, &encoded_array[0]);
|
||||
ct_test(pTest,
|
||||
memcmp(&real_array, &encoded_array, sizeof(real_array)) == 0);
|
||||
ct_test(
|
||||
pTest, memcmp(&real_array, &encoded_array, sizeof(real_array)) == 0);
|
||||
|
||||
/* a real will take up 4 octects plus a one octet tag */
|
||||
apdu_len = encode_application_real(&apdu[0], value);
|
||||
@@ -1867,11 +1881,11 @@ static void testBACDCodeReal(Test *pTest)
|
||||
|
||||
static void testBACDCodeDouble(Test *pTest)
|
||||
{
|
||||
uint8_t double_array[8] = {0};
|
||||
uint8_t encoded_array[8] = {0};
|
||||
uint8_t double_array[8] = { 0 };
|
||||
uint8_t encoded_array[8] = { 0 };
|
||||
double value = 42.123;
|
||||
double decoded_value = 0.0;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, apdu_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t long_value = 0;
|
||||
@@ -1881,7 +1895,7 @@ static void testBACDCodeDouble(Test *pTest)
|
||||
ct_test(pTest, decoded_value == value);
|
||||
encode_bacnet_double(value, &encoded_array[0]);
|
||||
ct_test(pTest,
|
||||
memcmp(&double_array, &encoded_array, sizeof(double_array)) == 0);
|
||||
memcmp(&double_array, &encoded_array, sizeof(double_array)) == 0);
|
||||
|
||||
/* a real will take up 4 octects plus a one octet tag */
|
||||
apdu_len = encode_application_double(&apdu[0], value);
|
||||
@@ -1900,11 +1914,11 @@ static void testBACDCodeDouble(Test *pTest)
|
||||
|
||||
static void testBACDCodeUnsignedValue(Test *pTest, uint32_t value)
|
||||
{
|
||||
uint8_t array[5] = {0};
|
||||
uint8_t encoded_array[5] = {0};
|
||||
uint8_t array[5] = { 0 };
|
||||
uint8_t encoded_array[5] = { 0 };
|
||||
uint32_t decoded_value = 0;
|
||||
int len;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
|
||||
@@ -1914,7 +1928,7 @@ static void testBACDCodeUnsignedValue(Test *pTest, uint32_t value)
|
||||
ct_test(pTest, decoded_value == value);
|
||||
if (decoded_value != value) {
|
||||
printf("value=%lu decoded_value=%lu\n", (unsigned long)value,
|
||||
(unsigned long)decoded_value);
|
||||
(unsigned long)decoded_value);
|
||||
print_apdu(&array[0], sizeof(array));
|
||||
}
|
||||
encode_application_unsigned(&encoded_array[0], decoded_value);
|
||||
@@ -1946,7 +1960,7 @@ static void testBACDCodeUnsigned(Test *pTest)
|
||||
|
||||
static void testBACnetUnsigned(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint32_t value = 0, test_value = 0;
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
@@ -1962,11 +1976,11 @@ static void testBACnetUnsigned(Test *pTest)
|
||||
|
||||
static void testBACDCodeSignedValue(Test *pTest, int32_t value)
|
||||
{
|
||||
uint8_t array[5] = {0};
|
||||
uint8_t encoded_array[5] = {0};
|
||||
uint8_t array[5] = { 0 };
|
||||
uint8_t encoded_array[5] = { 0 };
|
||||
int32_t decoded_value = 0;
|
||||
int len = 0;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
int diff = 0;
|
||||
@@ -1977,16 +1991,16 @@ static void testBACDCodeSignedValue(Test *pTest, int32_t value)
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT);
|
||||
ct_test(pTest, decoded_value == value);
|
||||
if (decoded_value != value) {
|
||||
printf("value=%ld decoded_value=%ld\n", (long)value,
|
||||
(long)decoded_value);
|
||||
printf(
|
||||
"value=%ld decoded_value=%ld\n", (long)value, (long)decoded_value);
|
||||
print_apdu(&array[0], sizeof(array));
|
||||
}
|
||||
encode_application_signed(&encoded_array[0], decoded_value);
|
||||
diff = memcmp(&array[0], &encoded_array[0], sizeof(array));
|
||||
ct_test(pTest, diff == 0);
|
||||
if (diff) {
|
||||
printf("value=%ld decoded_value=%ld\n", (long)value,
|
||||
(long)decoded_value);
|
||||
printf(
|
||||
"value=%ld decoded_value=%ld\n", (long)value, (long)decoded_value);
|
||||
print_apdu(&array[0], sizeof(array));
|
||||
print_apdu(&encoded_array[0], sizeof(array));
|
||||
}
|
||||
@@ -2026,7 +2040,7 @@ static void testBACDCodeSigned(Test *pTest)
|
||||
|
||||
static void testBACnetSigned(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
@@ -2046,11 +2060,11 @@ static void testBACnetSigned(Test *pTest)
|
||||
|
||||
static void testBACDCodeOctetString(Test *pTest)
|
||||
{
|
||||
uint8_t array[MAX_APDU] = {0};
|
||||
uint8_t encoded_array[MAX_APDU] = {0};
|
||||
uint8_t array[MAX_APDU] = { 0 };
|
||||
uint8_t encoded_array[MAX_APDU] = { 0 };
|
||||
BACNET_OCTET_STRING octet_string;
|
||||
BACNET_OCTET_STRING test_octet_string;
|
||||
uint8_t test_value[MAX_APDU] = {""};
|
||||
uint8_t test_value[MAX_APDU] = { "" };
|
||||
int i; /* for loop counter */
|
||||
int apdu_len;
|
||||
int len;
|
||||
@@ -2067,7 +2081,7 @@ static void testBACDCodeOctetString(Test *pTest)
|
||||
len += decode_octet_string(&array[len], len_value, &test_octet_string);
|
||||
ct_test(pTest, apdu_len == len);
|
||||
diff = memcmp(octetstring_value(&octet_string), &test_value[0],
|
||||
octetstring_length(&octet_string));
|
||||
octetstring_length(&octet_string));
|
||||
ct_test(pTest, diff == 0);
|
||||
|
||||
for (i = 0; i < (MAX_APDU - 6); i++) {
|
||||
@@ -2076,17 +2090,17 @@ static void testBACDCodeOctetString(Test *pTest)
|
||||
ct_test(pTest, status == true);
|
||||
apdu_len =
|
||||
encode_application_octet_string(&encoded_array[0], &octet_string);
|
||||
len = decode_tag_number_and_value(&encoded_array[0], &tag_number,
|
||||
&len_value);
|
||||
len = decode_tag_number_and_value(
|
||||
&encoded_array[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OCTET_STRING);
|
||||
len += decode_octet_string(&encoded_array[len], len_value,
|
||||
&test_octet_string);
|
||||
len += decode_octet_string(
|
||||
&encoded_array[len], len_value, &test_octet_string);
|
||||
if (apdu_len != len) {
|
||||
printf("test octet string=#%d\n", i);
|
||||
}
|
||||
ct_test(pTest, apdu_len == len);
|
||||
diff = memcmp(octetstring_value(&octet_string), &test_value[0],
|
||||
octetstring_length(&octet_string));
|
||||
octetstring_length(&octet_string));
|
||||
if (diff) {
|
||||
printf("test octet string=#%d\n", i);
|
||||
}
|
||||
@@ -2098,11 +2112,11 @@ static void testBACDCodeOctetString(Test *pTest)
|
||||
|
||||
static void testBACDCodeCharacterString(Test *pTest)
|
||||
{
|
||||
uint8_t array[MAX_APDU] = {0};
|
||||
uint8_t encoded_array[MAX_APDU] = {0};
|
||||
uint8_t array[MAX_APDU] = { 0 };
|
||||
uint8_t encoded_array[MAX_APDU] = { 0 };
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_CHARACTER_STRING test_char_string;
|
||||
char test_value[MAX_APDU] = {""};
|
||||
char test_value[MAX_APDU] = { "" };
|
||||
int i; /* for loop counter */
|
||||
int apdu_len;
|
||||
int len;
|
||||
@@ -2119,26 +2133,26 @@ static void testBACDCodeCharacterString(Test *pTest)
|
||||
len += decode_character_string(&array[len], len_value, &test_char_string);
|
||||
ct_test(pTest, apdu_len == len);
|
||||
diff = memcmp(characterstring_value(&char_string), &test_value[0],
|
||||
characterstring_length(&char_string));
|
||||
characterstring_length(&char_string));
|
||||
ct_test(pTest, diff == 0);
|
||||
for (i = 0; i < MAX_CHARACTER_STRING_BYTES - 1; i++) {
|
||||
test_value[i] = 'S';
|
||||
test_value[i + 1] = '\0';
|
||||
status = characterstring_init_ansi(&char_string, test_value);
|
||||
ct_test(pTest, status == true);
|
||||
apdu_len = encode_application_character_string(&encoded_array[0],
|
||||
&char_string);
|
||||
len = decode_tag_number_and_value(&encoded_array[0], &tag_number,
|
||||
&len_value);
|
||||
apdu_len = encode_application_character_string(
|
||||
&encoded_array[0], &char_string);
|
||||
len = decode_tag_number_and_value(
|
||||
&encoded_array[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING);
|
||||
len += decode_character_string(&encoded_array[len], len_value,
|
||||
&test_char_string);
|
||||
len += decode_character_string(
|
||||
&encoded_array[len], len_value, &test_char_string);
|
||||
if (apdu_len != len) {
|
||||
printf("test string=#%d apdu_len=%d len=%d\n", i, apdu_len, len);
|
||||
}
|
||||
ct_test(pTest, apdu_len == len);
|
||||
diff = memcmp(characterstring_value(&char_string), &test_value[0],
|
||||
characterstring_length(&char_string));
|
||||
characterstring_length(&char_string));
|
||||
if (diff) {
|
||||
printf("test string=#%d\n", i);
|
||||
}
|
||||
@@ -2150,8 +2164,8 @@ static void testBACDCodeCharacterString(Test *pTest)
|
||||
|
||||
static void testBACDCodeObject(Test *pTest)
|
||||
{
|
||||
uint8_t object_array[4] = {0};
|
||||
uint8_t encoded_array[4] = {0};
|
||||
uint8_t object_array[4] = { 0 };
|
||||
uint8_t encoded_array[4] = { 0 };
|
||||
uint16_t type = OBJECT_BINARY_INPUT;
|
||||
uint16_t decoded_type = OBJECT_ANALOG_OUTPUT;
|
||||
uint32_t instance = 123;
|
||||
@@ -2162,18 +2176,19 @@ static void testBACDCodeObject(Test *pTest)
|
||||
ct_test(pTest, decoded_type == type);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
encode_bacnet_object_id(&object_array[0], type, instance);
|
||||
ct_test(pTest, memcmp(&object_array[0], &encoded_array[0],
|
||||
sizeof(object_array)) == 0);
|
||||
ct_test(pTest,
|
||||
memcmp(&object_array[0], &encoded_array[0], sizeof(object_array)) == 0);
|
||||
for (type = 0; type < 1024; type++) {
|
||||
for (instance = 0; instance <= BACNET_MAX_INSTANCE; instance += 1024) {
|
||||
encode_bacnet_object_id(&encoded_array[0], type, instance);
|
||||
decode_object_id(&encoded_array[0], &decoded_type,
|
||||
&decoded_instance);
|
||||
decode_object_id(
|
||||
&encoded_array[0], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == type);
|
||||
ct_test(pTest, decoded_instance == instance);
|
||||
encode_bacnet_object_id(&object_array[0], type, instance);
|
||||
ct_test(pTest, memcmp(&object_array[0], &encoded_array[0],
|
||||
sizeof(object_array)) == 0);
|
||||
ct_test(pTest,
|
||||
memcmp(&object_array[0], &encoded_array[0],
|
||||
sizeof(object_array)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2182,8 +2197,8 @@ static void testBACDCodeObject(Test *pTest)
|
||||
|
||||
static void testBACDCodeMaxSegsApdu(Test *pTest)
|
||||
{
|
||||
int max_segs[8] = {0, 2, 4, 8, 16, 32, 64, 65};
|
||||
int max_apdu[6] = {50, 128, 206, 480, 1024, 1476};
|
||||
int max_segs[8] = { 0, 2, 4, 8, 16, 32, 64, 65 };
|
||||
int max_apdu[6] = { 50, 128, 206, 480, 1024, 1476 };
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
uint8_t octet = 0;
|
||||
@@ -2203,7 +2218,7 @@ static void testBACDCodeBitString(Test *pTest)
|
||||
uint8_t bit = 0;
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_BIT_STRING decoded_bit_string;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
int len = 0;
|
||||
@@ -2646,8 +2661,8 @@ static void testObjectIDContextDecodes(Test *pTest)
|
||||
inLen = encode_context_object_id(apdu, large_context_tag, in_type, in_id);
|
||||
outLen =
|
||||
decode_context_object_id(apdu, large_context_tag, &out_type, &out_id);
|
||||
outLen2 = decode_context_object_id(apdu, large_context_tag - 1, &out_type,
|
||||
&out_id);
|
||||
outLen2 = decode_context_object_id(
|
||||
apdu, large_context_tag - 1, &out_type, &out_id);
|
||||
|
||||
ct_test(pTest, inLen == outLen);
|
||||
ct_test(pTest, in_type == out_type);
|
||||
@@ -2739,7 +2754,7 @@ static void testOctetStringContextDecodes(Test *pTest)
|
||||
BACNET_OCTET_STRING in;
|
||||
BACNET_OCTET_STRING out;
|
||||
|
||||
uint8_t initData[] = {0xde, 0xad, 0xbe, 0xef};
|
||||
uint8_t initData[] = { 0xde, 0xad, 0xbe, 0xef };
|
||||
|
||||
octetstring_init(&in, initData, sizeof(initData));
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACDCODE_H
|
||||
#define BACDCODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacreal.h"
|
||||
#include "bacnet/bits.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* from clause 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool context_specific,
|
||||
uint32_t len_value_type);
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_opening_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
int encode_closing_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
int decode_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t * tag_number);
|
||||
int decode_tag_number_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
uint8_t * tag_number);
|
||||
int decode_tag_number_and_value(
|
||||
uint8_t * apdu,
|
||||
uint8_t * tag_number,
|
||||
uint32_t * value);
|
||||
int decode_tag_number_and_value_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
uint8_t * tag_number,
|
||||
uint32_t * value);
|
||||
/* returns true if the tag is an opening tag and matches */
|
||||
bool decode_is_opening_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
/* returns true if the tag is a closing tag and matches */
|
||||
bool decode_is_closing_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
/* returns true if the tag is context specific and matches */
|
||||
bool decode_is_context_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
bool decode_is_context_tag_with_length(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int *tag_length);
|
||||
/* returns true if the tag is an opening tag */
|
||||
bool decode_is_opening_tag(
|
||||
uint8_t * apdu);
|
||||
/* returns true if the tag is a closing tag */
|
||||
bool decode_is_closing_tag(
|
||||
uint8_t * apdu);
|
||||
|
||||
/* from clause 20.2.2 Encoding of a Null Value */
|
||||
int encode_application_null(
|
||||
uint8_t * apdu);
|
||||
int encode_context_null(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
|
||||
/* from clause 20.2.3 Encoding of a Boolean Value */
|
||||
int encode_application_boolean(
|
||||
uint8_t * apdu,
|
||||
bool boolean_value);
|
||||
bool decode_boolean(
|
||||
uint32_t len_value);
|
||||
int encode_context_boolean(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool boolean_value);
|
||||
bool decode_context_boolean(
|
||||
uint8_t * apdu);
|
||||
|
||||
int decode_context_boolean2(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool * boolean_value);
|
||||
|
||||
/* from clause 20.2.10 Encoding of a Bit String Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
|
||||
int decode_context_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bitstring(
|
||||
uint8_t * apdu,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
int encode_application_bitstring(
|
||||
uint8_t * apdu,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
int encode_context_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
|
||||
/* from clause 20.2.6 Encoding of a Real Number Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_real(
|
||||
uint8_t * apdu,
|
||||
float value);
|
||||
int encode_context_real(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
float value);
|
||||
|
||||
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_double(
|
||||
uint8_t * apdu,
|
||||
double value);
|
||||
|
||||
int encode_context_double(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
double value);
|
||||
|
||||
/* from clause 20.2.14 Encoding of an Object Identifier Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_object_id(
|
||||
uint8_t * apdu,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int decode_object_id_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int decode_context_object_id(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int encode_bacnet_object_id(
|
||||
uint8_t * apdu,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
int encode_context_object_id(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
int encode_application_object_id(
|
||||
uint8_t * apdu,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
|
||||
/* from clause 20.2.8 Encoding of an Octet String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_octet_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int encode_application_octet_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int encode_context_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int decode_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int decode_context_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
|
||||
|
||||
/* from clause 20.2.9 Encoding of a Character String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
uint32_t encode_bacnet_character_string_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t max_apdu,
|
||||
uint8_t encoding,
|
||||
char *pString,
|
||||
uint32_t length);
|
||||
int encode_bacnet_character_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int encode_application_character_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int encode_context_character_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int decode_character_string(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int decode_context_character_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
|
||||
|
||||
/* from clause 20.2.4 Encoding of an Unsigned Integer Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_context_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t value);
|
||||
int encode_application_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int decode_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint32_t * value);
|
||||
int decode_context_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t * value);
|
||||
|
||||
/* from clause 20.2.5 Encoding of a Signed Integer Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_signed(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int encode_application_signed(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int encode_context_signed(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int32_t value);
|
||||
int decode_signed(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
int32_t * value);
|
||||
int decode_context_signed(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int32_t * value);
|
||||
|
||||
|
||||
/* from clause 20.2.11 Encoding of an Enumerated Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint32_t * value);
|
||||
int decode_context_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_value,
|
||||
uint32_t * value);
|
||||
int encode_bacnet_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_application_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_context_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t value);
|
||||
|
||||
/* from clause 20.2.13 Encoding of a Time Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int encode_application_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_bacnet_time_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_TIME * btime);
|
||||
int encode_context_time(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME * btime);
|
||||
int decode_application_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_context_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME * btime);
|
||||
|
||||
|
||||
/* BACnet Date */
|
||||
/* year = years since 1900 */
|
||||
/* month 1=Jan */
|
||||
/* day = day of month */
|
||||
/* wday 1=Monday...7=Sunday */
|
||||
|
||||
/* from clause 20.2.12 Encoding of a Date Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int encode_application_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int encode_context_date(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_date_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_application_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_context_date(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DATE * bdate);
|
||||
|
||||
/* from clause 20.1.2.4 max-segments-accepted */
|
||||
/* and clause 20.1.2.5 max-APDU-length-accepted */
|
||||
/* returns the encoded octet */
|
||||
uint8_t encode_max_segs_max_apdu(
|
||||
int max_segs,
|
||||
int max_apdu);
|
||||
int decode_max_segs(
|
||||
uint8_t octet);
|
||||
int decode_max_apdu(
|
||||
uint8_t octet);
|
||||
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_simple_ack(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
uint8_t service_choice);
|
||||
|
||||
int encode_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
BACNET_ADDRESS * destination);
|
||||
int decode_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
BACNET_ADDRESS * destination);
|
||||
int encode_context_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ADDRESS * destination);
|
||||
int decode_context_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ADDRESS * destination);
|
||||
|
||||
/* from clause 20.2.1.2 Tag Number */
|
||||
/* true if extended tag numbering is used */
|
||||
#define IS_EXTENDED_TAG_NUMBER(x) ((x & 0xF0) == 0xF0)
|
||||
|
||||
/* from clause 20.2.1.3.1 Primitive Data */
|
||||
/* true if the extended value is used */
|
||||
#define IS_EXTENDED_VALUE(x) ((x & 0x07) == 5)
|
||||
|
||||
/* from clause 20.2.1.1 Class */
|
||||
/* true if the tag is context specific */
|
||||
#define IS_CONTEXT_SPECIFIC(x) ((x & BIT3) == BIT3)
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* true if the tag is an opening tag */
|
||||
#define IS_OPENING_TAG(x) ((x & 0x07) == 6)
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* true if the tag is a closing tag */
|
||||
#define IS_CLOSING_TAG(x) ((x & 0x07) == 7)
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void test_BACDCode(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACDEF_H
|
||||
#define BACDEF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
/* Silence the warnings about unsafe versions of library functions */
|
||||
/* as we need to keep the code portable */
|
||||
#pragma warning( disable : 4996)
|
||||
#endif
|
||||
|
||||
/* This stack implements this version of BACnet */
|
||||
#define BACNET_PROTOCOL_VERSION 1
|
||||
/* Although this stack can implement a later revision,
|
||||
* sometimes another revision is desired */
|
||||
#ifndef BACNET_PROTOCOL_REVISION
|
||||
#define BACNET_PROTOCOL_REVISION 19
|
||||
#endif
|
||||
|
||||
/* there are a few dependencies on the BACnet Protocol-Revision */
|
||||
#if (BACNET_PROTOCOL_REVISION == 0)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 18
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 35
|
||||
#elif (BACNET_PROTOCOL_REVISION == 1)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 21
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 37
|
||||
#elif (BACNET_PROTOCOL_REVISION == 2)
|
||||
/* from 135-2001 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 23
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 3)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 23
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 4)
|
||||
/* from 135-2004 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 25
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 5)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 30
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 6)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 7)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 8)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 9)
|
||||
/* from 135-2008 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 38
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 10)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 11)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 12)
|
||||
/* from 135-2010 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 13)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 53
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 14) || (BACNET_PROTOCOL_REVISION == 15)
|
||||
/* from 135-2012 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 55
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 16)
|
||||
/* Addendum 135-2012an, 135-2012at, 135-2012au,
|
||||
135-2012av, 135-2012aw, 135-2012ax, 135-2012az */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 56
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 17)
|
||||
/* Addendum 135-2012ai */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 57
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 18) || (BACNET_PROTOCOL_REVISION == 19)
|
||||
/* from 135-2016 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 60
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 44
|
||||
#else
|
||||
#error MAX_ASHRAE_OBJECT_TYPE and MAX_BACNET_SERVICES_SUPPORTED not defined!
|
||||
#endif
|
||||
|
||||
/* largest BACnet Instance Number */
|
||||
/* Also used as a device instance number wildcard address */
|
||||
#define BACNET_MAX_INSTANCE (0x3FFFFF)
|
||||
#define BACNET_INSTANCE_BITS 22
|
||||
/* large BACnet Object Type */
|
||||
#define BACNET_MAX_OBJECT (0x3FF)
|
||||
/* Array index 0=size of array, n=array element n, MAX=all array elements */
|
||||
/* 32-bit MAX, to use with uint32_t */
|
||||
#define BACNET_ARRAY_ALL 0xFFFFFFFFU
|
||||
/* For device object property references with no device id defined */
|
||||
#define BACNET_NO_DEV_ID 0xFFFFFFFFu
|
||||
#define BACNET_NO_DEV_TYPE 0xFFFFu
|
||||
/* Priority Array for commandable objects */
|
||||
#define BACNET_NO_PRIORITY 0
|
||||
#define BACNET_MIN_PRIORITY 1
|
||||
#define BACNET_MAX_PRIORITY 16
|
||||
|
||||
#define BACNET_BROADCAST_NETWORK (0xFFFF)
|
||||
/* Any size MAC address should be allowed which is less than or
|
||||
equal to 7 bytes. The IPv6 addresses are planned to be handled
|
||||
outside this area. */
|
||||
/* FIXME: mac[] only needs to be as big as our local datalink MAC */
|
||||
#define MAX_MAC_LEN 7
|
||||
struct BACnet_Device_Address {
|
||||
/* mac_len = 0 is a broadcast address */
|
||||
uint8_t mac_len;
|
||||
/* note: MAC for IP addresses uses 4 bytes for addr, 2 bytes for port */
|
||||
/* use de/encode_unsigned32/16 for re/storing the IP address */
|
||||
uint8_t mac[MAX_MAC_LEN];
|
||||
/* DNET,DLEN,DADR or SNET,SLEN,SADR */
|
||||
/* the following are used if the device is behind a router */
|
||||
/* net = 0 indicates local */
|
||||
uint16_t net; /* BACnet network number */
|
||||
/* LEN = 0 denotes broadcast MAC ADR and ADR field is absent */
|
||||
/* LEN > 0 specifies length of ADR field */
|
||||
uint8_t len; /* length of MAC address */
|
||||
uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */
|
||||
};
|
||||
typedef struct BACnet_Device_Address BACNET_ADDRESS;
|
||||
/* define a MAC address for manipulation */
|
||||
struct BACnet_MAC_Address {
|
||||
uint8_t len; /* length of MAC address */
|
||||
uint8_t adr[MAX_MAC_LEN];
|
||||
};
|
||||
typedef struct BACnet_MAC_Address BACNET_MAC_ADDRESS;
|
||||
|
||||
/* note: with microprocessors having lots more code space than memory,
|
||||
it might be better to have a packed encoding with a library to
|
||||
easily access the data. */
|
||||
typedef struct BACnet_Object_Id {
|
||||
uint16_t type;
|
||||
uint32_t instance;
|
||||
} BACNET_OBJECT_ID;
|
||||
|
||||
#define MAX_NPDU (1+1+2+1+MAX_MAC_LEN+2+1+MAX_MAC_LEN+1+1+2)
|
||||
#define MAX_PDU (MAX_APDU + MAX_NPDU)
|
||||
|
||||
#define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) ((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | ((bacnet_object_instance) & BACNET_MAX_INSTANCE))
|
||||
#define BACNET_INSTANCE(bacnet_object_id_num) ((bacnet_object_id_num)&BACNET_MAX_INSTANCE)
|
||||
#define BACNET_TYPE(bacnet_object_id_num) (((bacnet_object_id_num) >> BACNET_INSTANCE_BITS ) & BACNET_MAX_OBJECT)
|
||||
|
||||
#define BACNET_STATUS_OK (0)
|
||||
#define BACNET_STATUS_ERROR (-1)
|
||||
#define BACNET_STATUS_ABORT (-2)
|
||||
#define BACNET_STATUS_REJECT (-3)
|
||||
|
||||
#endif
|
||||
@@ -33,16 +33,16 @@
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacdcode.h"
|
||||
#include "npdu.h"
|
||||
#include "timestamp.h"
|
||||
#include "bacdevobjpropref.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
|
||||
/** @file bacdevobjpropref.c BACnet Application Device Object (Property)
|
||||
* Reference */
|
||||
|
||||
int bacapp_encode_context_device_obj_property_ref(
|
||||
uint8_t *apdu, uint8_t tag_number,
|
||||
int bacapp_encode_context_device_obj_property_ref(uint8_t *apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
@@ -78,12 +78,11 @@ int bacapp_encode_device_obj_property_ref(
|
||||
|
||||
/* object-identifier [0] BACnetObjectIdentifier */
|
||||
len = encode_context_object_id(&apdu[apdu_len], 0,
|
||||
(int)value->objectIdentifier.type,
|
||||
value->objectIdentifier.instance);
|
||||
(int)value->objectIdentifier.type, value->objectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
/* property-identifier [1] BACnetPropertyIdentifier */
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 1,
|
||||
value->propertyIdentifier);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[apdu_len], 1, value->propertyIdentifier);
|
||||
apdu_len += len;
|
||||
/* property-array-index [2] Unsigned OPTIONAL */
|
||||
/* Check if needed before inserting */
|
||||
@@ -97,8 +96,8 @@ int bacapp_encode_device_obj_property_ref(
|
||||
* omit */
|
||||
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
|
||||
len = encode_context_object_id(&apdu[apdu_len], 3,
|
||||
(int)value->deviceIdentifier.type,
|
||||
value->deviceIdentifier.instance);
|
||||
(int)value->deviceIdentifier.type,
|
||||
value->deviceIdentifier.instance);
|
||||
apdu_len += len;
|
||||
}
|
||||
return apdu_len;
|
||||
@@ -122,9 +121,10 @@ int bacapp_decode_device_obj_property_ref(
|
||||
uint32_t enumValue;
|
||||
|
||||
/* object-identifier [0] BACnetObjectIdentifier */
|
||||
if (-1 == (len = decode_context_object_id(
|
||||
&apdu[apdu_len], 0, &value->objectIdentifier.type,
|
||||
&value->objectIdentifier.instance))) {
|
||||
if (-1 ==
|
||||
(len = decode_context_object_id(&apdu[apdu_len], 0,
|
||||
&value->objectIdentifier.type,
|
||||
&value->objectIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
@@ -138,8 +138,9 @@ int bacapp_decode_device_obj_property_ref(
|
||||
/* property-array-index [2] Unsigned OPTIONAL */
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2) &&
|
||||
!decode_is_closing_tag(&apdu[apdu_len])) {
|
||||
if (-1 == (len = decode_context_unsigned(&apdu[apdu_len], 2,
|
||||
&value->arrayIndex))) {
|
||||
if (-1 ==
|
||||
(len = decode_context_unsigned(
|
||||
&apdu[apdu_len], 2, &value->arrayIndex))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
@@ -149,9 +150,10 @@ int bacapp_decode_device_obj_property_ref(
|
||||
/* device-identifier [3] BACnetObjectIdentifier OPTIONAL */
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 3) &&
|
||||
!decode_is_closing_tag(&apdu[apdu_len])) {
|
||||
if (-1 == (len = decode_context_object_id(
|
||||
&apdu[apdu_len], 3, &value->deviceIdentifier.type,
|
||||
&value->deviceIdentifier.instance))) {
|
||||
if (-1 ==
|
||||
(len = decode_context_object_id(&apdu[apdu_len], 3,
|
||||
&value->deviceIdentifier.type,
|
||||
&value->deviceIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
@@ -163,8 +165,8 @@ int bacapp_decode_device_obj_property_ref(
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_device_obj_property_ref(
|
||||
uint8_t *apdu, uint8_t tag_number,
|
||||
int bacapp_decode_context_device_obj_property_ref(uint8_t *apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len = 0;
|
||||
@@ -195,8 +197,8 @@ int bacapp_decode_context_device_obj_property_ref(
|
||||
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
object-identifier [1] BACnetObjectIdentifier
|
||||
}*/
|
||||
int bacapp_encode_context_device_obj_ref(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
int bacapp_encode_context_device_obj_ref(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -217,8 +219,8 @@ int bacapp_encode_context_device_obj_ref(uint8_t *apdu, uint8_t tag_number,
|
||||
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
object-identifier [1] BACnetObjectIdentifier
|
||||
}*/
|
||||
int bacapp_encode_device_obj_ref(uint8_t *apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
int bacapp_encode_device_obj_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -229,14 +231,13 @@ int bacapp_encode_device_obj_ref(uint8_t *apdu,
|
||||
* omit */
|
||||
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
|
||||
len = encode_context_object_id(&apdu[apdu_len], 0,
|
||||
(int)value->deviceIdentifier.type,
|
||||
value->deviceIdentifier.instance);
|
||||
(int)value->deviceIdentifier.type,
|
||||
value->deviceIdentifier.instance);
|
||||
apdu_len += len;
|
||||
}
|
||||
/* object-identifier [1] BACnetObjectIdentifier */
|
||||
len = encode_context_object_id(&apdu[apdu_len], 1,
|
||||
(int)value->objectIdentifier.type,
|
||||
value->objectIdentifier.instance);
|
||||
(int)value->objectIdentifier.type, value->objectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
@@ -246,8 +247,8 @@ int bacapp_encode_device_obj_ref(uint8_t *apdu,
|
||||
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
object-identifier [1] BACnetObjectIdentifier
|
||||
}*/
|
||||
int bacapp_decode_device_obj_ref(uint8_t *apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
int bacapp_decode_device_obj_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -255,9 +256,10 @@ int bacapp_decode_device_obj_ref(uint8_t *apdu,
|
||||
/* device-identifier [0] BACnetObjectIdentifier OPTIONAL */
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0) &&
|
||||
!decode_is_closing_tag(&apdu[apdu_len])) {
|
||||
if (-1 == (len = decode_context_object_id(
|
||||
&apdu[apdu_len], 0, &value->deviceIdentifier.type,
|
||||
&value->deviceIdentifier.instance))) {
|
||||
if (-1 ==
|
||||
(len = decode_context_object_id(&apdu[apdu_len], 0,
|
||||
&value->deviceIdentifier.type,
|
||||
&value->deviceIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
@@ -266,9 +268,10 @@ int bacapp_decode_device_obj_ref(uint8_t *apdu,
|
||||
value->deviceIdentifier.instance = BACNET_NO_DEV_ID;
|
||||
}
|
||||
/* object-identifier [1] BACnetObjectIdentifier */
|
||||
if (-1 == (len = decode_context_object_id(
|
||||
&apdu[apdu_len], 1, &value->objectIdentifier.type,
|
||||
&value->objectIdentifier.instance))) {
|
||||
if (-1 ==
|
||||
(len = decode_context_object_id(&apdu[apdu_len], 1,
|
||||
&value->objectIdentifier.type,
|
||||
&value->objectIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
@@ -276,8 +279,8 @@ int bacapp_decode_device_obj_ref(uint8_t *apdu,
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_device_obj_ref(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
int bacapp_decode_context_device_obj_ref(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -306,11 +309,11 @@ int bacapp_decode_context_device_obj_ref(uint8_t *apdu, uint8_t tag_number,
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
static void testDevObjPropRef(Test *pTest,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *inData)
|
||||
static void testDevObjPropRef(
|
||||
Test *pTest, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *inData)
|
||||
{
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE outData;
|
||||
uint8_t buffer[MAX_APDU] = {0};
|
||||
uint8_t buffer[MAX_APDU] = { 0 };
|
||||
int inLen = 0;
|
||||
int outLen = 0;
|
||||
|
||||
@@ -322,10 +325,10 @@ static void testDevObjPropRef(Test *pTest,
|
||||
/* decode */
|
||||
outLen = bacapp_decode_device_obj_property_ref(buffer, &outData);
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData->objectIdentifier.instance ==
|
||||
outData.objectIdentifier.instance);
|
||||
ct_test(pTest,
|
||||
inData->objectIdentifier.type == outData.objectIdentifier.type);
|
||||
inData->objectIdentifier.instance == outData.objectIdentifier.instance);
|
||||
ct_test(
|
||||
pTest, inData->objectIdentifier.type == outData.objectIdentifier.type);
|
||||
ct_test(pTest, inData->propertyIdentifier == outData.propertyIdentifier);
|
||||
if (inData->arrayIndex != BACNET_ARRAY_ALL) {
|
||||
ct_test(pTest, inData->arrayIndex == outData.arrayIndex);
|
||||
@@ -333,10 +336,11 @@ static void testDevObjPropRef(Test *pTest,
|
||||
ct_test(pTest, outData.arrayIndex == BACNET_ARRAY_ALL);
|
||||
}
|
||||
if (inData->deviceIdentifier.type == OBJECT_DEVICE) {
|
||||
ct_test(pTest, inData->deviceIdentifier.instance ==
|
||||
outData.deviceIdentifier.instance);
|
||||
ct_test(pTest,
|
||||
inData->deviceIdentifier.type == outData.deviceIdentifier.type);
|
||||
inData->deviceIdentifier.instance ==
|
||||
outData.deviceIdentifier.instance);
|
||||
ct_test(pTest,
|
||||
inData->deviceIdentifier.type == outData.deviceIdentifier.type);
|
||||
} else {
|
||||
ct_test(pTest, outData.deviceIdentifier.instance == BACNET_NO_DEV_ID);
|
||||
ct_test(pTest, outData.deviceIdentifier.type == BACNET_NO_DEV_TYPE);
|
||||
@@ -394,10 +398,10 @@ static void testDevIdRef(Test *pTest)
|
||||
inLen = bacapp_encode_device_obj_ref(buffer, &inData);
|
||||
outLen = bacapp_decode_device_obj_ref(buffer, &outData);
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.deviceIdentifier.instance ==
|
||||
outData.deviceIdentifier.instance);
|
||||
ct_test(pTest,
|
||||
inData.deviceIdentifier.type == outData.deviceIdentifier.type);
|
||||
inData.deviceIdentifier.instance == outData.deviceIdentifier.instance);
|
||||
ct_test(
|
||||
pTest, inData.deviceIdentifier.type == outData.deviceIdentifier.type);
|
||||
}
|
||||
|
||||
void testBACnetDeviceObjectPropertyReference(Test *pTest)
|
||||
@@ -0,0 +1,101 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 John Minack
|
||||
*
|
||||
* 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 _BAC_DEV_PROP_REF_H_
|
||||
#define _BAC_DEV_PROP_REF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct BACnetDeviceObjectPropertyReference {
|
||||
BACNET_OBJECT_ID objectIdentifier;
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t arrayIndex;
|
||||
BACNET_OBJECT_ID deviceIdentifier;
|
||||
} BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE;
|
||||
|
||||
/** BACnetDeviceObjectReference structure.
|
||||
* If the optional deviceIdentifier is not provided, then this refers
|
||||
* to an object inside this Device.
|
||||
*/
|
||||
typedef struct BACnetDeviceObjectReference {
|
||||
BACNET_OBJECT_ID deviceIdentifier; /**< Optional, for external device. */
|
||||
BACNET_OBJECT_ID objectIdentifier;
|
||||
} BACNET_DEVICE_OBJECT_REFERENCE;
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_encode_context_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_context_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
|
||||
int bacapp_encode_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_encode_context_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_context_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBACnetDeviceObjectPropertyReference(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,18 +32,19 @@
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include "bacenum.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacerror.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
|
||||
/** @file bacerror.c Encode/Decode BACnet Errors */
|
||||
|
||||
/* encode service */
|
||||
int bacerror_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE service,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
int bacerror_encode_apdu(uint8_t *apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE service,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
@@ -62,9 +63,10 @@ int bacerror_encode_apdu(uint8_t *apdu, uint8_t invoke_id,
|
||||
|
||||
#if !BACNET_SVC_SERVER
|
||||
/* decode the application class and code */
|
||||
int bacerror_decode_error_class_and_code(uint8_t *apdu, unsigned apdu_len,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
int bacerror_decode_error_class_and_code(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
{
|
||||
int len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
@@ -73,43 +75,50 @@ int bacerror_decode_error_class_and_code(uint8_t *apdu, unsigned apdu_len,
|
||||
|
||||
if (apdu_len) {
|
||||
/* error class */
|
||||
len += decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED)
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
return 0;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
|
||||
if (error_class)
|
||||
if (error_class) {
|
||||
*error_class = (BACNET_ERROR_CLASS)decoded_value;
|
||||
}
|
||||
/* error code */
|
||||
len += decode_tag_number_and_value(&apdu[len], &tag_number,
|
||||
&len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED)
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
return 0;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
|
||||
if (error_code)
|
||||
if (error_code) {
|
||||
*error_code = (BACNET_ERROR_CODE)decoded_value;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int bacerror_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE *service,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
int bacerror_decode_service_request(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE *service,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
if (apdu_len > 2) {
|
||||
if (invoke_id)
|
||||
if (invoke_id) {
|
||||
*invoke_id = apdu[0];
|
||||
if (service)
|
||||
}
|
||||
if (service) {
|
||||
*service = (BACNET_CONFIRMED_SERVICE)apdu[1];
|
||||
}
|
||||
/* decode the application class and code */
|
||||
len = bacerror_decode_error_class_and_code(&apdu[2], apdu_len - 2,
|
||||
error_class, error_code);
|
||||
len = bacerror_decode_error_class_and_code(
|
||||
&apdu[2], apdu_len - 2, error_class, error_code);
|
||||
}
|
||||
|
||||
return len;
|
||||
@@ -122,10 +131,12 @@ int bacerror_decode_service_request(uint8_t *apdu, unsigned apdu_len,
|
||||
#include "ctest.h"
|
||||
|
||||
/* decode the whole APDU - mainly used for unit testing */
|
||||
int bacerror_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE *service,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
int bacerror_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t *invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE *service,
|
||||
BACNET_ERROR_CLASS *error_class,
|
||||
BACNET_ERROR_CODE *error_code)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
@@ -137,8 +148,7 @@ int bacerror_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
return -1;
|
||||
if (apdu_len > 1) {
|
||||
len = bacerror_decode_service_request(&apdu[1], apdu_len - 1,
|
||||
invoke_id, service,
|
||||
error_class, error_code);
|
||||
invoke_id, service, error_class, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,7 +157,7 @@ int bacerror_decode_apdu(uint8_t *apdu, unsigned apdu_len, uint8_t *invoke_id,
|
||||
|
||||
void testBACError(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[480] = {0};
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
@@ -159,14 +169,13 @@ void testBACError(Test *pTest)
|
||||
BACNET_ERROR_CLASS test_error_class = 0;
|
||||
BACNET_ERROR_CODE test_error_code = 0;
|
||||
|
||||
len = bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class,
|
||||
error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&apdu[0], invoke_id, service, error_class, error_code);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
|
||||
len =
|
||||
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_service,
|
||||
&test_error_class, &test_error_code);
|
||||
len = bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_service, &test_error_class, &test_error_code);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
@@ -175,19 +184,18 @@ void testBACError(Test *pTest)
|
||||
|
||||
/* change type to get negative response */
|
||||
apdu[0] = PDU_TYPE_ABORT;
|
||||
len =
|
||||
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_service,
|
||||
&test_error_class, &test_error_code);
|
||||
len = bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_service, &test_error_class, &test_error_code);
|
||||
ct_test(pTest, len == -1);
|
||||
|
||||
/* test NULL APDU */
|
||||
len = bacerror_decode_apdu(NULL, apdu_len, &test_invoke_id, &test_service,
|
||||
&test_error_class, &test_error_code);
|
||||
&test_error_class, &test_error_code);
|
||||
ct_test(pTest, len == -1);
|
||||
|
||||
/* force a zero length */
|
||||
len = bacerror_decode_apdu(&apdu[0], 0, &test_invoke_id, &test_service,
|
||||
&test_error_class, &test_error_code);
|
||||
&test_error_class, &test_error_code);
|
||||
ct_test(pTest, len == 0);
|
||||
|
||||
/* check them all... */
|
||||
@@ -196,13 +204,12 @@ void testBACError(Test *pTest)
|
||||
error_class++) {
|
||||
for (error_code = 0; error_code < ERROR_CODE_PROPRIETARY_FIRST;
|
||||
error_code++) {
|
||||
len = bacerror_encode_apdu(&apdu[0], invoke_id, service,
|
||||
error_class, error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&apdu[0], invoke_id, service, error_class, error_code);
|
||||
apdu_len = len;
|
||||
ct_test(pTest, len != 0);
|
||||
len = bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_service, &test_error_class,
|
||||
&test_error_code);
|
||||
&test_service, &test_error_class, &test_error_code);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
@@ -216,13 +223,12 @@ void testBACError(Test *pTest)
|
||||
service = 255;
|
||||
error_class = ERROR_CLASS_PROPRIETARY_LAST;
|
||||
error_code = ERROR_CODE_PROPRIETARY_LAST;
|
||||
len = bacerror_encode_apdu(&apdu[0], invoke_id, service, error_class,
|
||||
error_code);
|
||||
len = bacerror_encode_apdu(
|
||||
&apdu[0], invoke_id, service, error_class, error_code);
|
||||
apdu_len = len;
|
||||
ct_test(pTest, len != 0);
|
||||
len =
|
||||
bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_service,
|
||||
&test_error_class, &test_error_code);
|
||||
len = bacerror_decode_apdu(&apdu[0], apdu_len, &test_invoke_id,
|
||||
&test_service, &test_error_class, &test_error_code);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
@@ -0,0 +1,73 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACERROR_H
|
||||
#define BACERROR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacerror_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE service,
|
||||
BACNET_ERROR_CLASS error_class,
|
||||
BACNET_ERROR_CODE error_code);
|
||||
|
||||
int bacerror_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE * service,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
int bacerror_decode_error_class_and_code(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
int bacerror_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
uint8_t * invoke_id,
|
||||
BACNET_CONFIRMED_SERVICE * service,
|
||||
BACNET_ERROR_CLASS * error_class,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
void testBACError(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -36,8 +36,8 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "config.h"
|
||||
#include "bacint.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacint.h"
|
||||
|
||||
/** @file bacint.c Encode/Decode Integer Types */
|
||||
|
||||
@@ -164,10 +164,11 @@ int decode_signed8(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80)
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFFFFFF00;
|
||||
else
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
*value |= ((int32_t)(((int32_t)apdu[0]) & 0x000000ff));
|
||||
}
|
||||
|
||||
@@ -186,10 +187,11 @@ int decode_signed16(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80)
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFFFF0000;
|
||||
else
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
*value |= ((int32_t)((((int32_t)apdu[0]) << 8) & 0x0000ff00));
|
||||
*value |= ((int32_t)(((int32_t)apdu[1]) & 0x000000ff));
|
||||
}
|
||||
@@ -210,10 +212,11 @@ int decode_signed24(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80)
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFF000000;
|
||||
else
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
*value |= ((int32_t)((((int32_t)apdu[0]) << 16) & 0x00ff0000));
|
||||
*value |= ((int32_t)((((int32_t)apdu[1]) << 8) & 0x0000ff00));
|
||||
*value |= ((int32_t)(((int32_t)apdu[2]) & 0x000000ff));
|
||||
@@ -253,7 +256,7 @@ int decode_signed32(uint8_t *apdu, int32_t *value)
|
||||
|
||||
static void testBACnetUnsigned16(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint16_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -269,7 +272,7 @@ static void testBACnetUnsigned16(Test *pTest)
|
||||
|
||||
static void testBACnetUnsigned24(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -285,7 +288,7 @@ static void testBACnetUnsigned24(Test *pTest)
|
||||
|
||||
static void testBACnetUnsigned32(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -301,7 +304,7 @@ static void testBACnetUnsigned32(Test *pTest)
|
||||
|
||||
static void testBACnetSigned8(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -317,7 +320,7 @@ static void testBACnetSigned8(Test *pTest)
|
||||
|
||||
static void testBACnetSigned16(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -333,7 +336,7 @@ static void testBACnetSigned16(Test *pTest)
|
||||
|
||||
static void testBACnetSigned24(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -347,7 +350,7 @@ static void testBACnetSigned24(Test *pTest)
|
||||
|
||||
static void testBACnetSigned32(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = {0};
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACINT_H
|
||||
#define BACINT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* unsigned value encoding and decoding */
|
||||
int encode_unsigned16(
|
||||
uint8_t * apdu,
|
||||
uint16_t value);
|
||||
int decode_unsigned16(
|
||||
uint8_t * apdu,
|
||||
uint16_t * value);
|
||||
int encode_unsigned24(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int decode_unsigned24(
|
||||
uint8_t * apdu,
|
||||
uint32_t * value);
|
||||
int encode_unsigned32(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int decode_unsigned32(
|
||||
uint8_t * apdu,
|
||||
uint32_t * value);
|
||||
#ifdef UINT64_MAX
|
||||
int decode_unsigned64(
|
||||
uint8_t * buffer,
|
||||
uint64_t * value);
|
||||
#endif
|
||||
/* signed value encoding and decoding */
|
||||
int encode_signed8(
|
||||
uint8_t * apdu,
|
||||
int8_t value);
|
||||
int decode_signed8(
|
||||
uint8_t * apdu,
|
||||
int32_t * value);
|
||||
int encode_signed16(
|
||||
uint8_t * apdu,
|
||||
int16_t value);
|
||||
int decode_signed16(
|
||||
uint8_t * apdu,
|
||||
int32_t * value);
|
||||
int encode_signed24(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int decode_signed24(
|
||||
uint8_t * apdu,
|
||||
int32_t * value);
|
||||
int encode_signed32(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int decode_signed32(
|
||||
uint8_t * apdu,
|
||||
int32_t * value);
|
||||
#ifdef UINT64_MAX
|
||||
int encode_unsigned64(
|
||||
uint8_t * buffer,
|
||||
uint64_t value);
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBACnetIntegers(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -36,33 +36,34 @@
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#include "bacprop.h"
|
||||
#include "bacnet/bacprop.h"
|
||||
|
||||
/** @file bacprop.c Lookup BACnet Property Tags */
|
||||
|
||||
PROP_TAG_DATA bacnet_object_device_property_tag_map[] = {
|
||||
{PROP_OBJECT_IDENTIFIER, BACNET_APPLICATION_TAG_OBJECT_ID},
|
||||
{PROP_OBJECT_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING},
|
||||
{PROP_OBJECT_TYPE, BACNET_APPLICATION_TAG_ENUMERATED},
|
||||
{PROP_SYSTEM_STATUS, BACNET_APPLICATION_TAG_ENUMERATED},
|
||||
{PROP_VENDOR_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING},
|
||||
{PROP_VENDOR_IDENTIFIER, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{PROP_MODEL_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING},
|
||||
{PROP_FIRMWARE_REVISION, BACNET_APPLICATION_TAG_CHARACTER_STRING},
|
||||
{PROP_APPLICATION_SOFTWARE_VERSION,
|
||||
BACNET_APPLICATION_TAG_CHARACTER_STRING},
|
||||
{PROP_PROTOCOL_VERSION, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{PROP_PROTOCOL_CONFORMANCE_CLASS, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{PROP_PROTOCOL_SERVICES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING},
|
||||
{PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING},
|
||||
{PROP_MAX_APDU_LENGTH_ACCEPTED, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{PROP_SEGMENTATION_SUPPORTED, BACNET_APPLICATION_TAG_ENUMERATED},
|
||||
{PROP_APDU_TIMEOUT, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{PROP_NUMBER_OF_APDU_RETRIES, BACNET_APPLICATION_TAG_UNSIGNED_INT},
|
||||
{-1, -1}};
|
||||
{ PROP_OBJECT_IDENTIFIER, BACNET_APPLICATION_TAG_OBJECT_ID },
|
||||
{ PROP_OBJECT_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING },
|
||||
{ PROP_OBJECT_TYPE, BACNET_APPLICATION_TAG_ENUMERATED },
|
||||
{ PROP_SYSTEM_STATUS, BACNET_APPLICATION_TAG_ENUMERATED },
|
||||
{ PROP_VENDOR_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING },
|
||||
{ PROP_VENDOR_IDENTIFIER, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ PROP_MODEL_NAME, BACNET_APPLICATION_TAG_CHARACTER_STRING },
|
||||
{ PROP_FIRMWARE_REVISION, BACNET_APPLICATION_TAG_CHARACTER_STRING },
|
||||
{ PROP_APPLICATION_SOFTWARE_VERSION,
|
||||
BACNET_APPLICATION_TAG_CHARACTER_STRING },
|
||||
{ PROP_PROTOCOL_VERSION, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ PROP_PROTOCOL_CONFORMANCE_CLASS, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ PROP_PROTOCOL_SERVICES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING },
|
||||
{ PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, BACNET_APPLICATION_TAG_BIT_STRING },
|
||||
{ PROP_MAX_APDU_LENGTH_ACCEPTED, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ PROP_SEGMENTATION_SUPPORTED, BACNET_APPLICATION_TAG_ENUMERATED },
|
||||
{ PROP_APDU_TIMEOUT, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ PROP_NUMBER_OF_APDU_RETRIES, BACNET_APPLICATION_TAG_UNSIGNED_INT },
|
||||
{ -1, -1 }
|
||||
};
|
||||
|
||||
signed bacprop_tag_by_index_default(PROP_TAG_DATA* data_list, signed index,
|
||||
signed default_ret)
|
||||
signed bacprop_tag_by_index_default(
|
||||
PROP_TAG_DATA *data_list, signed index, signed default_ret)
|
||||
{
|
||||
signed pUnsigned = 0;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 John Goulah
|
||||
*
|
||||
* 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 BACPROP_H
|
||||
#define BACPROP_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct {
|
||||
signed prop_id; /* index number that matches the text */
|
||||
signed tag_id; /* text pair - use NULL to end the list */
|
||||
} PROP_TAG_DATA;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
signed bacprop_tag_by_index_default(
|
||||
PROP_TAG_DATA * data_list,
|
||||
signed index,
|
||||
signed default_ret);
|
||||
|
||||
signed bacprop_property_tag(
|
||||
BACNET_OBJECT_TYPE type,
|
||||
signed prop);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -31,11 +31,11 @@
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include "bacdcode.h"
|
||||
#include "npdu.h"
|
||||
#include "timestamp.h"
|
||||
#include "bacpropstates.h"
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
#include "bacnet/bacpropstates.h"
|
||||
|
||||
/** @file bacpropstates.c Encode/Decode BACnet Application Property States */
|
||||
|
||||
@@ -61,104 +61,116 @@ int bacapp_decode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
break;
|
||||
|
||||
case BINARY_VALUE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.binaryValue = (BACNET_BINARY_PV)enumValue;
|
||||
break;
|
||||
|
||||
case EVENT_TYPE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.eventType = (BACNET_EVENT_TYPE)enumValue;
|
||||
break;
|
||||
|
||||
case POLARITY:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.polarity = (BACNET_POLARITY)enumValue;
|
||||
break;
|
||||
|
||||
case PROGRAM_CHANGE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.programChange = (BACNET_PROGRAM_REQUEST)enumValue;
|
||||
break;
|
||||
|
||||
case PROGRAM_STATE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.programState = (BACNET_PROGRAM_STATE)enumValue;
|
||||
break;
|
||||
|
||||
case REASON_FOR_HALT:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.programError = (BACNET_PROGRAM_ERROR)enumValue;
|
||||
break;
|
||||
|
||||
case RELIABILITY:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.reliability = (BACNET_RELIABILITY)enumValue;
|
||||
break;
|
||||
|
||||
case STATE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.state = (BACNET_EVENT_STATE)enumValue;
|
||||
break;
|
||||
|
||||
case SYSTEM_STATUS:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.systemStatus = (BACNET_DEVICE_STATUS)enumValue;
|
||||
break;
|
||||
|
||||
case UNITS:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.units = (BACNET_ENGINEERING_UNITS)enumValue;
|
||||
break;
|
||||
|
||||
case UNSIGNED_VALUE:
|
||||
if (-1 == (section_length =
|
||||
decode_unsigned(&apdu[len], len_value_type,
|
||||
&value->state.unsignedValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_unsigned(&apdu[len], len_value_type,
|
||||
&value->state.unsignedValue))) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_MODE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.lifeSafetyMode = (BACNET_LIFE_SAFETY_MODE)enumValue;
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_STATE:
|
||||
if (-1 == (section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value_type, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.lifeSafetyState = (BACNET_LIFE_SAFETY_STATE)enumValue;
|
||||
@@ -172,8 +184,8 @@ int bacapp_decode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
return len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_property_state(uint8_t *apdu, uint8_t tag_number,
|
||||
BACNET_PROPERTY_STATE *value)
|
||||
int bacapp_decode_context_property_state(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_PROPERTY_STATE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
@@ -204,43 +216,43 @@ int bacapp_encode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
if (value && apdu) {
|
||||
switch (value->tag) {
|
||||
case BOOLEAN_VALUE:
|
||||
len = encode_context_boolean(&apdu[0], 0,
|
||||
value->state.booleanValue);
|
||||
len = encode_context_boolean(
|
||||
&apdu[0], 0, value->state.booleanValue);
|
||||
break;
|
||||
|
||||
case BINARY_VALUE:
|
||||
len = encode_context_enumerated(&apdu[0], 1,
|
||||
value->state.binaryValue);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 1, value->state.binaryValue);
|
||||
break;
|
||||
|
||||
case EVENT_TYPE:
|
||||
len = encode_context_enumerated(&apdu[0], 2,
|
||||
value->state.eventType);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 2, value->state.eventType);
|
||||
break;
|
||||
|
||||
case POLARITY:
|
||||
len = encode_context_enumerated(&apdu[0], 3,
|
||||
value->state.polarity);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 3, value->state.polarity);
|
||||
break;
|
||||
|
||||
case PROGRAM_CHANGE:
|
||||
len = encode_context_enumerated(&apdu[0], 4,
|
||||
value->state.programChange);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 4, value->state.programChange);
|
||||
break;
|
||||
|
||||
case PROGRAM_STATE:
|
||||
len = encode_context_enumerated(&apdu[0], 5,
|
||||
value->state.programState);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 5, value->state.programState);
|
||||
break;
|
||||
|
||||
case REASON_FOR_HALT:
|
||||
len = encode_context_enumerated(&apdu[0], 6,
|
||||
value->state.programError);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 6, value->state.programError);
|
||||
break;
|
||||
|
||||
case RELIABILITY:
|
||||
len = encode_context_enumerated(&apdu[0], 7,
|
||||
value->state.reliability);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 7, value->state.reliability);
|
||||
break;
|
||||
|
||||
case STATE:
|
||||
@@ -249,8 +261,8 @@ int bacapp_encode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
break;
|
||||
|
||||
case SYSTEM_STATUS:
|
||||
len = encode_context_enumerated(&apdu[0], 9,
|
||||
value->state.systemStatus);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 9, value->state.systemStatus);
|
||||
break;
|
||||
|
||||
case UNITS:
|
||||
@@ -259,18 +271,18 @@ int bacapp_encode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
break;
|
||||
|
||||
case UNSIGNED_VALUE:
|
||||
len = encode_context_unsigned(&apdu[0], 11,
|
||||
value->state.unsignedValue);
|
||||
len = encode_context_unsigned(
|
||||
&apdu[0], 11, value->state.unsignedValue);
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_MODE:
|
||||
len = encode_context_enumerated(&apdu[0], 12,
|
||||
value->state.lifeSafetyMode);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 12, value->state.lifeSafetyMode);
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_STATE:
|
||||
len = encode_context_enumerated(&apdu[0], 13,
|
||||
value->state.lifeSafetyState);
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 13, value->state.lifeSafetyState);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -0,0 +1,90 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 John Minack
|
||||
*
|
||||
* 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 _BAC_PROP_STATES_H_
|
||||
#define _BAC_PROP_STATES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
|
||||
typedef enum {
|
||||
BOOLEAN_VALUE,
|
||||
BINARY_VALUE,
|
||||
EVENT_TYPE,
|
||||
POLARITY,
|
||||
PROGRAM_CHANGE,
|
||||
PROGRAM_STATE,
|
||||
REASON_FOR_HALT,
|
||||
RELIABILITY,
|
||||
STATE,
|
||||
SYSTEM_STATUS,
|
||||
UNITS,
|
||||
UNSIGNED_VALUE,
|
||||
LIFE_SAFETY_MODE,
|
||||
LIFE_SAFETY_STATE
|
||||
} BACNET_PROPERTY_STATE_TYPE;
|
||||
|
||||
typedef struct {
|
||||
BACNET_PROPERTY_STATE_TYPE tag;
|
||||
union {
|
||||
bool booleanValue;
|
||||
BACNET_BINARY_PV binaryValue;
|
||||
BACNET_EVENT_TYPE eventType;
|
||||
BACNET_POLARITY polarity;
|
||||
BACNET_PROGRAM_REQUEST programChange;
|
||||
BACNET_PROGRAM_STATE programState;
|
||||
BACNET_PROGRAM_ERROR programError;
|
||||
BACNET_RELIABILITY reliability;
|
||||
BACNET_EVENT_STATE state;
|
||||
BACNET_DEVICE_STATUS systemStatus;
|
||||
BACNET_ENGINEERING_UNITS units;
|
||||
uint32_t unsignedValue;
|
||||
BACNET_LIFE_SAFETY_MODE lifeSafetyMode;
|
||||
BACNET_LIFE_SAFETY_STATE lifeSafetyState;
|
||||
} state;
|
||||
} BACNET_PROPERTY_STATE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_decode_property_state(
|
||||
uint8_t * apdu,
|
||||
BACNET_PROPERTY_STATE * value);
|
||||
|
||||
int bacapp_decode_context_property_state(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_PROPERTY_STATE * value);
|
||||
|
||||
int bacapp_encode_property_state(
|
||||
uint8_t * apdu,
|
||||
BACNET_PROPERTY_STATE * value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -34,13 +34,13 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "bacenum.h"
|
||||
#include "bits.h"
|
||||
#include "bacstr.h"
|
||||
#include "bacint.h"
|
||||
#include "bacreal.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacreal.h"
|
||||
|
||||
/** @file bacreal.c Encode/Decode Floating Point (Real) Types */
|
||||
|
||||
@@ -208,8 +208,8 @@ int encode_bacnet_double(double value, uint8_t *apdu)
|
||||
return 8;
|
||||
}
|
||||
|
||||
int decode_context_double(uint8_t *apdu, uint8_t tag_number,
|
||||
double *double_value)
|
||||
int decode_context_double(
|
||||
uint8_t *apdu, uint8_t tag_number, double *double_value)
|
||||
{
|
||||
uint32_t len_value;
|
||||
int len = 0;
|
||||
@@ -234,7 +234,7 @@ int decode_context_double(uint8_t *apdu, uint8_t tag_number,
|
||||
void testBACreal(Test *pTest)
|
||||
{
|
||||
float real_value = 3.14159F, test_real_value = 0.0;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
len = encode_bacnet_real(real_value, &apdu[0]);
|
||||
@@ -247,7 +247,7 @@ void testBACreal(Test *pTest)
|
||||
void testBACdouble(Test *pTest)
|
||||
{
|
||||
double double_value = 3.1415927, test_double_value = 0.0;
|
||||
uint8_t apdu[MAX_APDU] = {0};
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
len = encode_bacnet_double(double_value, &apdu[0]);
|
||||
@@ -0,0 +1,79 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACREAL_H
|
||||
#define BACREAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int decode_real_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
float *real_value);
|
||||
|
||||
int decode_real(
|
||||
uint8_t * apdu,
|
||||
float *real_value);
|
||||
|
||||
int decode_context_real(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
float *real_value);
|
||||
int encode_bacnet_real(
|
||||
float value,
|
||||
uint8_t * apdu);
|
||||
int decode_double(
|
||||
uint8_t * apdu,
|
||||
double *real_value);
|
||||
int decode_context_double(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
double *double_value);
|
||||
int decode_double_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
double *double_value);
|
||||
|
||||
int encode_bacnet_double(
|
||||
double value,
|
||||
uint8_t * apdu);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
|
||||
void testBACreal(
|
||||
Test * pTest);
|
||||
void testBACdouble(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -35,12 +35,12 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h> /* for strlen */
|
||||
#include "config.h"
|
||||
#include "bacstr.h"
|
||||
#include "bits.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bits.h"
|
||||
#if PRINT_ENABLED
|
||||
#include <stdlib.h> /* for strtol */
|
||||
#include <ctype.h> /* for isalnum */
|
||||
#include <ctype.h> /* for isalnum */
|
||||
#endif
|
||||
|
||||
/** @file bacstr.c Manipulate Bit/Char/Octet Strings */
|
||||
@@ -55,8 +55,8 @@ void bitstring_init(BACNET_BIT_STRING *bit_string)
|
||||
}
|
||||
}
|
||||
|
||||
void bitstring_set_bit(BACNET_BIT_STRING *bit_string, uint8_t bit_number,
|
||||
bool value)
|
||||
void bitstring_set_bit(
|
||||
BACNET_BIT_STRING *bit_string, uint8_t bit_number, bool value)
|
||||
{
|
||||
uint8_t byte_number = bit_number / 8;
|
||||
uint8_t bit_mask = 1;
|
||||
@@ -127,8 +127,8 @@ uint8_t bitstring_octet(BACNET_BIT_STRING *bit_string, uint8_t octet_index)
|
||||
return octet;
|
||||
}
|
||||
|
||||
bool bitstring_set_octet(BACNET_BIT_STRING *bit_string, uint8_t index,
|
||||
uint8_t octet)
|
||||
bool bitstring_set_octet(
|
||||
BACNET_BIT_STRING *bit_string, uint8_t index, uint8_t octet)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
@@ -142,8 +142,8 @@ bool bitstring_set_octet(BACNET_BIT_STRING *bit_string, uint8_t index,
|
||||
return status;
|
||||
}
|
||||
|
||||
bool bitstring_set_bits_used(BACNET_BIT_STRING *bit_string, uint8_t bytes_used,
|
||||
uint8_t unused_bits)
|
||||
bool bitstring_set_bits_used(
|
||||
BACNET_BIT_STRING *bit_string, uint8_t bytes_used, uint8_t unused_bits)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
@@ -183,8 +183,8 @@ bool bitstring_copy(BACNET_BIT_STRING *dest, BACNET_BIT_STRING *src)
|
||||
}
|
||||
|
||||
/* returns true if the same length and contents */
|
||||
bool bitstring_same(BACNET_BIT_STRING *bitstring1,
|
||||
BACNET_BIT_STRING *bitstring2)
|
||||
bool bitstring_same(
|
||||
BACNET_BIT_STRING *bitstring1, BACNET_BIT_STRING *bitstring2)
|
||||
{
|
||||
int i = 0; /* loop counter */
|
||||
int bytes_used = 0;
|
||||
@@ -220,7 +220,7 @@ bool bitstring_same(BACNET_BIT_STRING *bitstring1,
|
||||
bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned index = 0; /* offset into buffer */
|
||||
unsigned index = 0; /* offset into buffer */
|
||||
uint8_t bit_number = 0;
|
||||
|
||||
if (bit_string) {
|
||||
@@ -262,10 +262,12 @@ bool bitstring_init_ascii(BACNET_BIT_STRING *bit_string, const char *ascii)
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using value=NULL */
|
||||
bool characterstring_init(BACNET_CHARACTER_STRING *char_string,
|
||||
uint8_t encoding, const char *value, size_t length)
|
||||
uint8_t encoding,
|
||||
const char *value,
|
||||
size_t length)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
size_t i; /* counter */
|
||||
size_t i; /* counter */
|
||||
|
||||
if (char_string) {
|
||||
char_string->length = 0;
|
||||
@@ -294,23 +296,22 @@ bool characterstring_init(BACNET_CHARACTER_STRING *char_string,
|
||||
return status;
|
||||
}
|
||||
|
||||
bool characterstring_init_ansi(BACNET_CHARACTER_STRING *char_string,
|
||||
const char *value)
|
||||
bool characterstring_init_ansi(
|
||||
BACNET_CHARACTER_STRING *char_string, const char *value)
|
||||
{
|
||||
return characterstring_init(char_string, CHARACTER_ANSI_X34, value,
|
||||
value ? strlen(value) : 0);
|
||||
return characterstring_init(
|
||||
char_string, CHARACTER_ANSI_X34, value, value ? strlen(value) : 0);
|
||||
}
|
||||
|
||||
bool characterstring_copy(BACNET_CHARACTER_STRING *dest,
|
||||
BACNET_CHARACTER_STRING *src)
|
||||
bool characterstring_copy(
|
||||
BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src)
|
||||
{
|
||||
return characterstring_init(dest, characterstring_encoding(src),
|
||||
characterstring_value(src),
|
||||
characterstring_length(src));
|
||||
characterstring_value(src), characterstring_length(src));
|
||||
}
|
||||
|
||||
bool characterstring_ansi_copy(char *dest, size_t dest_max_len,
|
||||
BACNET_CHARACTER_STRING *src)
|
||||
bool characterstring_ansi_copy(
|
||||
char *dest, size_t dest_max_len, BACNET_CHARACTER_STRING *src)
|
||||
{
|
||||
size_t i; /* counter */
|
||||
|
||||
@@ -330,8 +331,8 @@ bool characterstring_ansi_copy(char *dest, size_t dest_max_len,
|
||||
}
|
||||
|
||||
/* returns true if the character encoding and string contents are the same */
|
||||
bool characterstring_same(BACNET_CHARACTER_STRING *dest,
|
||||
BACNET_CHARACTER_STRING *src)
|
||||
bool characterstring_same(
|
||||
BACNET_CHARACTER_STRING *dest, BACNET_CHARACTER_STRING *src)
|
||||
{
|
||||
size_t i; /* counter */
|
||||
bool same_status = false;
|
||||
@@ -390,10 +391,10 @@ bool characterstring_ansi_same(BACNET_CHARACTER_STRING *dest, const char *src)
|
||||
}
|
||||
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool characterstring_append(BACNET_CHARACTER_STRING *char_string,
|
||||
const char *value, size_t length)
|
||||
bool characterstring_append(
|
||||
BACNET_CHARACTER_STRING *char_string, const char *value, size_t length)
|
||||
{
|
||||
size_t i; /* counter */
|
||||
size_t i; /* counter */
|
||||
bool status = false; /* return value */
|
||||
|
||||
if (char_string) {
|
||||
@@ -412,8 +413,8 @@ bool characterstring_append(BACNET_CHARACTER_STRING *char_string,
|
||||
/* This function sets a new length without changing the value.
|
||||
If length exceeds capacity, no modification happens and
|
||||
function returns false. */
|
||||
bool characterstring_truncate(BACNET_CHARACTER_STRING *char_string,
|
||||
size_t length)
|
||||
bool characterstring_truncate(
|
||||
BACNET_CHARACTER_STRING *char_string, size_t length)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
|
||||
@@ -476,8 +477,8 @@ uint8_t characterstring_encoding(BACNET_CHARACTER_STRING *char_string)
|
||||
}
|
||||
|
||||
/* returns the encoding. */
|
||||
bool characterstring_set_encoding(BACNET_CHARACTER_STRING *char_string,
|
||||
uint8_t encoding)
|
||||
bool characterstring_set_encoding(
|
||||
BACNET_CHARACTER_STRING *char_string, uint8_t encoding)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
@@ -503,7 +504,7 @@ X'20' - X'7E'.*/
|
||||
bool characterstring_printable(BACNET_CHARACTER_STRING *char_string)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
size_t i; /* counter */
|
||||
size_t i; /* counter */
|
||||
|
||||
if (char_string) {
|
||||
if (char_string->encoding == CHARACTER_ANSI_X34) {
|
||||
@@ -529,18 +530,17 @@ bool characterstring_printable(BACNET_CHARACTER_STRING *char_string)
|
||||
/* Basic UTF-8 manipulation routines
|
||||
by Jeff Bezanson
|
||||
placed in the public domain Fall 2005 */
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
|
||||
static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
|
||||
|
||||
/* based on the valid_utf8 routine from the PCRE library by Philip Hazel
|
||||
length is in bytes, since without knowing whether the string is valid
|
||||
@@ -583,40 +583,46 @@ bool utf8_isvalid(const char *str, size_t length)
|
||||
switch (ab) {
|
||||
/* Check for xx00 000x */
|
||||
case 1:
|
||||
if ((c & 0x3e) == 0)
|
||||
if ((c & 0x3e) == 0) {
|
||||
return false;
|
||||
}
|
||||
continue; /* We know there aren't any more bytes to check */
|
||||
|
||||
/* Check for 1110 0000, xx0x xxxx */
|
||||
case 2:
|
||||
if (c == 0xe0 && (*p & 0x20) == 0)
|
||||
if (c == 0xe0 && (*p & 0x20) == 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Check for 1111 0000, xx00 xxxx */
|
||||
case 3:
|
||||
if (c == 0xf0 && (*p & 0x30) == 0)
|
||||
if (c == 0xf0 && (*p & 0x30) == 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Check for 1111 1000, xx00 0xxx */
|
||||
case 4:
|
||||
if (c == 0xf8 && (*p & 0x38) == 0)
|
||||
if (c == 0xf8 && (*p & 0x38) == 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
/* Check for leading 0xfe or 0xff,
|
||||
and then for 1111 1100, xx00 00xx */
|
||||
case 5:
|
||||
if (c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0))
|
||||
if (c == 0xfe || c == 0xff || (c == 0xfc && (*p & 0x3c) == 0)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for valid bytes after the 2nd, if any; all must start 10 */
|
||||
while (--ab > 0) {
|
||||
if ((*(++p) & 0xc0) != 0x80)
|
||||
if ((*(++p) & 0xc0) != 0x80) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,11 +649,11 @@ bool characterstring_valid(BACNET_CHARACTER_STRING *char_string)
|
||||
#if BACNET_USE_OCTETSTRING
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using value=NULL */
|
||||
bool octetstring_init(BACNET_OCTET_STRING *octet_string, uint8_t *value,
|
||||
size_t length)
|
||||
bool octetstring_init(
|
||||
BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
size_t i; /* counter */
|
||||
size_t i; /* counter */
|
||||
|
||||
if (octet_string && (length <= MAX_OCTET_STRING_BYTES)) {
|
||||
octet_string->length = 0;
|
||||
@@ -674,11 +680,11 @@ bool octetstring_init(BACNET_OCTET_STRING *octet_string, uint8_t *value,
|
||||
#if PRINT_ENABLED
|
||||
/* converts an null terminated ASCII Hex string to an octet string.
|
||||
returns true if successfully converted and fits; false if too long */
|
||||
bool octetstring_init_ascii_hex(BACNET_OCTET_STRING *octet_string,
|
||||
const char *ascii_hex)
|
||||
bool octetstring_init_ascii_hex(
|
||||
BACNET_OCTET_STRING *octet_string, const char *ascii_hex)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned index = 0; /* offset into buffer */
|
||||
unsigned index = 0; /* offset into buffer */
|
||||
uint8_t value = 0;
|
||||
char hex_pair_string[3] = "";
|
||||
|
||||
@@ -721,14 +727,14 @@ bool octetstring_init_ascii_hex(BACNET_OCTET_STRING *octet_string,
|
||||
|
||||
bool octetstring_copy(BACNET_OCTET_STRING *dest, BACNET_OCTET_STRING *src)
|
||||
{
|
||||
return octetstring_init(dest, octetstring_value(src),
|
||||
octetstring_length(src));
|
||||
return octetstring_init(
|
||||
dest, octetstring_value(src), octetstring_length(src));
|
||||
}
|
||||
|
||||
/* returns the number of bytes copied, or 0 if the dest
|
||||
cannot hold entire octetstring value */
|
||||
size_t octetstring_copy_value(uint8_t *dest, size_t length,
|
||||
BACNET_OCTET_STRING *src)
|
||||
size_t octetstring_copy_value(
|
||||
uint8_t *dest, size_t length, BACNET_OCTET_STRING *src)
|
||||
{
|
||||
size_t bytes_copied = 0;
|
||||
size_t i; /* counter */
|
||||
@@ -746,10 +752,10 @@ size_t octetstring_copy_value(uint8_t *dest, size_t length,
|
||||
}
|
||||
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool octetstring_append(BACNET_OCTET_STRING *octet_string, uint8_t *value,
|
||||
size_t length)
|
||||
bool octetstring_append(
|
||||
BACNET_OCTET_STRING *octet_string, uint8_t *value, size_t length)
|
||||
{
|
||||
size_t i; /* counter */
|
||||
size_t i; /* counter */
|
||||
bool status = false; /* return value */
|
||||
|
||||
if (octet_string) {
|
||||
@@ -821,8 +827,8 @@ size_t octetstring_capacity(BACNET_OCTET_STRING *octet_string)
|
||||
}
|
||||
|
||||
/* returns true if the same length and contents */
|
||||
bool octetstring_value_same(BACNET_OCTET_STRING *octet_string1,
|
||||
BACNET_OCTET_STRING *octet_string2)
|
||||
bool octetstring_value_same(
|
||||
BACNET_OCTET_STRING *octet_string1, BACNET_OCTET_STRING *octet_string2)
|
||||
{
|
||||
size_t i = 0; /* loop counter */
|
||||
|
||||
@@ -905,7 +911,7 @@ void testBitString(Test *pTest)
|
||||
* be different */
|
||||
bitstring_set_bit(&bit_string2, 0, !bitstring_bit(&bit_string, 0));
|
||||
bitstring_set_bit(&bit_string3, max_bit - 1,
|
||||
!bitstring_bit(&bit_string, max_bit - 1));
|
||||
!bitstring_bit(&bit_string, max_bit - 1));
|
||||
ct_test(pTest, !bitstring_same(&bit_string, &bit_string2));
|
||||
ct_test(pTest, !bitstring_same(&bit_string, &bit_string3));
|
||||
}
|
||||
@@ -927,22 +933,22 @@ void testCharacterString(Test *pTest)
|
||||
status = characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, 0);
|
||||
ct_test(pTest, status == true);
|
||||
ct_test(pTest, characterstring_length(&bacnet_string) == 0);
|
||||
ct_test(pTest,
|
||||
characterstring_encoding(&bacnet_string) == CHARACTER_ANSI_X34);
|
||||
ct_test(
|
||||
pTest, characterstring_encoding(&bacnet_string) == CHARACTER_ANSI_X34);
|
||||
/* bounds check */
|
||||
status = characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL,
|
||||
characterstring_capacity(&bacnet_string) + 1);
|
||||
characterstring_capacity(&bacnet_string) + 1);
|
||||
ct_test(pTest, status == false);
|
||||
status = characterstring_truncate(
|
||||
&bacnet_string, characterstring_capacity(&bacnet_string) + 1);
|
||||
ct_test(pTest, status == false);
|
||||
status = characterstring_truncate(&bacnet_string,
|
||||
characterstring_capacity(&bacnet_string));
|
||||
status = characterstring_truncate(
|
||||
&bacnet_string, characterstring_capacity(&bacnet_string));
|
||||
ct_test(pTest, status == true);
|
||||
|
||||
test_length = strlen(test_value);
|
||||
status = characterstring_init(&bacnet_string, CHARACTER_ANSI_X34,
|
||||
&test_value[0], test_length);
|
||||
status = characterstring_init(
|
||||
&bacnet_string, CHARACTER_ANSI_X34, &test_value[0], test_length);
|
||||
ct_test(pTest, status == true);
|
||||
value = characterstring_value(&bacnet_string);
|
||||
length = characterstring_length(&bacnet_string);
|
||||
@@ -951,8 +957,8 @@ void testCharacterString(Test *pTest)
|
||||
ct_test(pTest, value[i] == test_value[i]);
|
||||
}
|
||||
test_length = strlen(test_append_value);
|
||||
status = characterstring_append(&bacnet_string, &test_append_value[0],
|
||||
test_length);
|
||||
status = characterstring_append(
|
||||
&bacnet_string, &test_append_value[0], test_length);
|
||||
strcat(test_append_string, test_value);
|
||||
strcat(test_append_string, test_append_value);
|
||||
test_length = strlen(test_append_string);
|
||||
@@ -986,17 +992,17 @@ void testOctetString(Test *pTest)
|
||||
ct_test(pTest, value[i] == 0);
|
||||
}
|
||||
/* bounds check */
|
||||
status = octetstring_init(&bacnet_string, NULL,
|
||||
octetstring_capacity(&bacnet_string) + 1);
|
||||
status = octetstring_init(
|
||||
&bacnet_string, NULL, octetstring_capacity(&bacnet_string) + 1);
|
||||
ct_test(pTest, status == false);
|
||||
status = octetstring_init(&bacnet_string, NULL,
|
||||
octetstring_capacity(&bacnet_string));
|
||||
status = octetstring_init(
|
||||
&bacnet_string, NULL, octetstring_capacity(&bacnet_string));
|
||||
ct_test(pTest, status == true);
|
||||
status = octetstring_truncate(&bacnet_string,
|
||||
octetstring_capacity(&bacnet_string) + 1);
|
||||
status = octetstring_truncate(
|
||||
&bacnet_string, octetstring_capacity(&bacnet_string) + 1);
|
||||
ct_test(pTest, status == false);
|
||||
status = octetstring_truncate(&bacnet_string,
|
||||
octetstring_capacity(&bacnet_string));
|
||||
status = octetstring_truncate(
|
||||
&bacnet_string, octetstring_capacity(&bacnet_string));
|
||||
ct_test(pTest, status == true);
|
||||
|
||||
test_length = strlen((char *)test_value);
|
||||
@@ -0,0 +1,207 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACSTR_H
|
||||
#define BACSTR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
/* bit strings
|
||||
They could be as large as 256/8=32 octets */
|
||||
typedef struct BACnet_Bit_String {
|
||||
uint8_t bits_used;
|
||||
uint8_t value[MAX_BITSTRING_BYTES];
|
||||
} BACNET_BIT_STRING;
|
||||
|
||||
typedef struct BACnet_Character_String {
|
||||
size_t length;
|
||||
uint8_t encoding;
|
||||
/* limit - 6 octets is the most our tag and type could be */
|
||||
char value[MAX_CHARACTER_STRING_BYTES];
|
||||
} BACNET_CHARACTER_STRING;
|
||||
|
||||
/* FIXME: convert the bacdcode library to use BACNET_OCTET_STRING
|
||||
for APDU buffer to prevent buffer overflows */
|
||||
typedef struct BACnet_Octet_String {
|
||||
size_t length;
|
||||
/* limit - 6 octets is the most our tag and type could be */
|
||||
uint8_t value[MAX_OCTET_STRING_BYTES];
|
||||
} BACNET_OCTET_STRING;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bitstring_init(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
void bitstring_set_bit(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bit_number,
|
||||
bool value);
|
||||
bool bitstring_bit(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bit_number);
|
||||
uint8_t bitstring_bits_used(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* returns the number of bytes that a bit string is using */
|
||||
uint8_t bitstring_bytes_used(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
uint8_t bitstring_bits_capacity(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* used for encoding and decoding from the APDU */
|
||||
uint8_t bitstring_octet(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t octet_index);
|
||||
bool bitstring_set_octet(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t index,
|
||||
uint8_t octet);
|
||||
bool bitstring_set_bits_used(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bytes_used,
|
||||
uint8_t unused_bits);
|
||||
bool bitstring_copy(
|
||||
BACNET_BIT_STRING * dest,
|
||||
BACNET_BIT_STRING * src);
|
||||
bool bitstring_same(
|
||||
BACNET_BIT_STRING * bitstring1,
|
||||
BACNET_BIT_STRING * bitstring2);
|
||||
bool bitstring_init_ascii(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
const char *ascii);
|
||||
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using length=0 */
|
||||
bool characterstring_init(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
uint8_t encoding,
|
||||
const char *value,
|
||||
size_t length);
|
||||
/* used for ANSI C-Strings */
|
||||
bool characterstring_init_ansi(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
const char *value);
|
||||
bool characterstring_copy(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
bool characterstring_ansi_copy(
|
||||
char *dest,
|
||||
size_t dest_max_len,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
/* returns true if the strings are the same length, encoding, value */
|
||||
bool characterstring_same(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
bool characterstring_ansi_same(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
const char *src);
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool characterstring_append(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
const char *value,
|
||||
size_t length);
|
||||
/* This function sets a new length without changing the value.
|
||||
If length exceeds capacity, no modification happens and
|
||||
function returns false. */
|
||||
bool characterstring_truncate(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
size_t length);
|
||||
bool characterstring_set_encoding(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
uint8_t encoding);
|
||||
/* Returns the value */
|
||||
char *characterstring_value(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
/* returns the length */
|
||||
size_t characterstring_length(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
uint8_t characterstring_encoding(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
size_t characterstring_capacity(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool characterstring_printable(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool characterstring_valid(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool utf8_isvalid(
|
||||
const char *str,
|
||||
size_t length);
|
||||
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using length=0 */
|
||||
bool octetstring_init(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
uint8_t * value,
|
||||
size_t length);
|
||||
#ifdef PRINT_ENABLED
|
||||
/* converts an null terminated ASCII Hex string to an octet string.
|
||||
returns true if successfully converted and fits; false if too long */
|
||||
bool octetstring_init_ascii_hex(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
const char *ascii_hex);
|
||||
#endif
|
||||
bool octetstring_copy(
|
||||
BACNET_OCTET_STRING * dest,
|
||||
BACNET_OCTET_STRING * src);
|
||||
size_t octetstring_copy_value(
|
||||
uint8_t * dest,
|
||||
size_t length,
|
||||
BACNET_OCTET_STRING * src);
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool octetstring_append(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
uint8_t * value,
|
||||
size_t length);
|
||||
/* This function sets a new length without changing the value.
|
||||
If length exceeds capacity, no modification happens and
|
||||
function returns false. */
|
||||
bool octetstring_truncate(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
size_t length);
|
||||
/* Returns the value */
|
||||
uint8_t *octetstring_value(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
/* Returns the length.*/
|
||||
size_t octetstring_length(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
size_t octetstring_capacity(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
/* returns true if the same length and contents */
|
||||
bool octetstring_value_same(
|
||||
BACNET_OCTET_STRING * octet_string1,
|
||||
BACNET_OCTET_STRING * octet_string2);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBACnetStrings(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 BACTEXT_H
|
||||
#define BACTEXT_H
|
||||
|
||||
/* tiny implementations have no need to print */
|
||||
#if PRINT_ENABLED
|
||||
#define BACTEXT_PRINT_ENABLED
|
||||
#else
|
||||
#ifdef TEST
|
||||
#define BACTEXT_PRINT_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/indtext.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
const char *bactext_confirmed_service_name(
|
||||
unsigned index);
|
||||
const char *bactext_unconfirmed_service_name(
|
||||
unsigned index);
|
||||
const char *bactext_application_tag_name(
|
||||
unsigned index);
|
||||
bool bactext_application_tag_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_object_type_name(
|
||||
unsigned index);
|
||||
bool bactext_object_type_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_notify_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_property_name(
|
||||
unsigned index);
|
||||
const char *bactext_property_name_default(
|
||||
unsigned index,
|
||||
const char *default_string);
|
||||
bool bactext_property_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_engineering_unit_name(
|
||||
unsigned index);
|
||||
bool bactext_engineering_unit_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_reject_reason_name(
|
||||
unsigned index);
|
||||
const char *bactext_abort_reason_name(
|
||||
unsigned index);
|
||||
const char *bactext_error_class_name(
|
||||
unsigned index);
|
||||
const char *bactext_error_code_name(
|
||||
unsigned index);
|
||||
unsigned bactext_property_id(
|
||||
const char *name);
|
||||
const char *bactext_month_name(
|
||||
unsigned index);
|
||||
const char *bactext_week_of_month_name(
|
||||
unsigned index);
|
||||
const char *bactext_day_of_week_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_state_name(
|
||||
unsigned index);
|
||||
const char *bactext_binary_present_value_name(
|
||||
unsigned index);
|
||||
const char *bactext_binary_polarity_name(
|
||||
unsigned index);
|
||||
bool bactext_binary_present_value_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_reliability_name(
|
||||
unsigned index);
|
||||
const char *bactext_device_status_name(
|
||||
unsigned index);
|
||||
const char *bactext_segmentation_name(
|
||||
unsigned index);
|
||||
bool bactext_segmentation_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_node_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_character_string_encoding_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_transition_name(
|
||||
unsigned index);
|
||||
bool bactext_event_transition_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
|
||||
const char *bactext_days_of_week_name(
|
||||
unsigned index);
|
||||
bool bactext_days_of_week_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
|
||||
const char *bactext_network_layer_msg_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_life_safety_state_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_device_communications_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_operation_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_in_progress(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_transition(
|
||||
unsigned index);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacdcode.h"
|
||||
#include "bactimevalue.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bactimevalue.h"
|
||||
|
||||
int bacapp_encode_time_value(uint8_t* apdu, BACNET_TIME_VALUE* value)
|
||||
int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -51,8 +51,8 @@ int bacapp_encode_time_value(uint8_t* apdu, BACNET_TIME_VALUE* value)
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_time_value(uint8_t* apdu, uint8_t tag_number,
|
||||
BACNET_TIME_VALUE* value)
|
||||
int bacapp_encode_context_time_value(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
@@ -69,45 +69,50 @@ int bacapp_encode_context_time_value(uint8_t* apdu, uint8_t tag_number,
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_time_value(uint8_t* apdu, BACNET_TIME_VALUE* value)
|
||||
int bacapp_decode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = decode_application_time(&apdu[apdu_len], &value->Time);
|
||||
if (len <= 0)
|
||||
if (len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value);
|
||||
if (len <= 0)
|
||||
if (len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_time_value(uint8_t* apdu, uint8_t tag_number,
|
||||
BACNET_TIME_VALUE* value)
|
||||
int bacapp_decode_context_time_value(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number))
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
section_length = bacapp_decode_time_value(&apdu[len], value);
|
||||
if (section_length > 0)
|
||||
if (section_length > 0) {
|
||||
len += section_length;
|
||||
else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number))
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
else
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef _BAC_TIME_VALUE_H_
|
||||
#define _BAC_TIME_VALUE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
|
||||
typedef struct {
|
||||
BACNET_TIME Time;
|
||||
BACNET_APPLICATION_DATA_VALUE Value;
|
||||
} BACNET_TIME_VALUE;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_time_value(uint8_t * apdu,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_encode_context_time_value(uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_decode_time_value(uint8_t * apdu,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_decode_context_time_value(uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic BBMD for BVLC IPv6 handler
|
||||
*
|
||||
* @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 BVLC6_HANDLER_H
|
||||
#define BVLC6_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/datalink/bvlc6.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* user application function prototypes */
|
||||
int bvlc6_handler(
|
||||
BACNET_IP6_ADDRESS *addr,
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_register_with_bbmd(
|
||||
BACNET_IP6_ADDRESS *bbmd_addr,
|
||||
uint32_t vmac_src,
|
||||
uint16_t time_to_live_seconds);
|
||||
uint16_t bvlc6_get_last_result(
|
||||
void);
|
||||
uint8_t bvlc6_get_function_code(
|
||||
void);
|
||||
void bvlc6_init(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -36,11 +36,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
#include "bacdef.h"
|
||||
#include "keylist.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/basic/sys/keylist.h"
|
||||
/* me! */
|
||||
#include "vmac.h"
|
||||
#include "bacnet/basic/bbmd6/vmac.h"
|
||||
|
||||
/** @file
|
||||
Handle VMAC address binding */
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date 2016
|
||||
*/
|
||||
#ifndef VMAC_H
|
||||
#define VMAC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* define the max MAC as big as IPv6 + port number */
|
||||
#define VMAC_MAC_MAX 18
|
||||
/**
|
||||
* VMAC data structure
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
struct vmac_data {
|
||||
uint8_t mac[18];
|
||||
uint8_t mac_len;
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
unsigned int VMAC_Count(void);
|
||||
struct vmac_data *VMAC_Find_By_Key(uint32_t device_id);
|
||||
bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id);
|
||||
bool VMAC_Add(uint32_t device_id, struct vmac_data *pVMAC);
|
||||
bool VMAC_Delete(uint32_t device_id);
|
||||
bool VMAC_Different(
|
||||
struct vmac_data *vmac1,
|
||||
struct vmac_data *vmac2);
|
||||
bool VMAC_Match(
|
||||
struct vmac_data *vmac1,
|
||||
struct vmac_data *vmac2);
|
||||
void VMAC_Cleanup(void);
|
||||
void VMAC_Init(void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testVMAC(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -36,12 +36,12 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "config.h"
|
||||
#include "bacaddr.h"
|
||||
#include "address.h"
|
||||
#include "bacdef.h"
|
||||
#include "bacdcode.h"
|
||||
#include "readrange.h"
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacaddr.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/readrange.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
|
||||
/* we are likely compiling the demo command line tools if print enabled */
|
||||
#if !defined(BACNET_ADDRESS_CACHE_FILE)
|
||||
@@ -69,12 +69,12 @@ static struct Address_Cache_Entry {
|
||||
|
||||
/* State flags for cache entries */
|
||||
|
||||
#define BAC_ADDR_IN_USE 1 /* Address cache entry in use */
|
||||
#define BAC_ADDR_IN_USE 1 /* Address cache entry in use */
|
||||
#define BAC_ADDR_BIND_REQ 2 /* Bind request outstanding for entry */
|
||||
#define BAC_ADDR_STATIC 4 /* Static address mapping - does not expire */
|
||||
#define BAC_ADDR_SHORT_TTL \
|
||||
8 /* Oppertunistaclly added address with short TTL \
|
||||
*/
|
||||
#define BAC_ADDR_STATIC 4 /* Static address mapping - does not expire */
|
||||
#define BAC_ADDR_SHORT_TTL \
|
||||
8 /* Oppertunistaclly added address with short TTL \
|
||||
*/
|
||||
#define BAC_ADDR_RESERVED 128 /* Freed up but held for caller to fill */
|
||||
|
||||
#define BAC_ADDR_SECS_1HOUR 3600 /* 60x60 */
|
||||
@@ -99,30 +99,38 @@ bool address_match(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
|
||||
uint8_t i = 0;
|
||||
uint8_t max_len = 0;
|
||||
|
||||
if (dest->mac_len != src->mac_len)
|
||||
if (dest->mac_len != src->mac_len) {
|
||||
return false;
|
||||
max_len = dest->mac_len;
|
||||
if (max_len > MAX_MAC_LEN)
|
||||
max_len = MAX_MAC_LEN;
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (dest->mac[i] != src->mac[i])
|
||||
return false;
|
||||
}
|
||||
if (dest->net != src->net)
|
||||
max_len = dest->mac_len;
|
||||
if (max_len > MAX_MAC_LEN) {
|
||||
max_len = MAX_MAC_LEN;
|
||||
}
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (dest->mac[i] != src->mac[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (dest->net != src->net) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* if local, ignore remaining fields */
|
||||
if (dest->net == 0)
|
||||
if (dest->net == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dest->len != src->len)
|
||||
if (dest->len != src->len) {
|
||||
return false;
|
||||
}
|
||||
max_len = dest->len;
|
||||
if (max_len > MAX_MAC_LEN)
|
||||
if (max_len > MAX_MAC_LEN) {
|
||||
max_len = MAX_MAC_LEN;
|
||||
}
|
||||
for (i = 0; i < max_len; i++) {
|
||||
if (dest->adr[i] != src->adr[i])
|
||||
if (dest->adr[i] != src->adr[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -175,8 +183,9 @@ static struct Address_Cache_Entry *address_remove_oldest(void)
|
||||
|
||||
pMatch = &Address_Cache[Top_Protected_Entry];
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ |
|
||||
BAC_ADDR_STATIC)) == BAC_ADDR_IN_USE) {
|
||||
if ((pMatch->Flags &
|
||||
(BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ | BAC_ADDR_STATIC)) ==
|
||||
BAC_ADDR_IN_USE) {
|
||||
if (pMatch->TimeToLive <= ulTime) { /* Shorter lived entry found */
|
||||
ulTime = pMatch->TimeToLive;
|
||||
pCandidate = pMatch;
|
||||
@@ -196,7 +205,7 @@ static struct Address_Cache_Entry *address_remove_oldest(void)
|
||||
pMatch = Address_Cache;
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
if ((pMatch->Flags &
|
||||
(BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ | BAC_ADDR_STATIC)) ==
|
||||
(BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ | BAC_ADDR_STATIC)) ==
|
||||
((uint8_t)(BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ))) {
|
||||
if (pMatch->TimeToLive <= ulTime) { /* Shorter lived entry found */
|
||||
ulTime = pMatch->TimeToLive;
|
||||
@@ -244,7 +253,7 @@ void address_mac_init(BACNET_MAC_ADDRESS *mac, uint8_t *adr, uint8_t len)
|
||||
*/
|
||||
bool address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, char *arg)
|
||||
{
|
||||
unsigned a[6] = {0}, p = 0;
|
||||
unsigned a[6] = { 0 }, p = 0;
|
||||
uint16_t port = 0;
|
||||
int c;
|
||||
bool status = false;
|
||||
@@ -268,7 +277,7 @@ bool address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, char *arg)
|
||||
status = true;
|
||||
} else {
|
||||
c = sscanf(arg, "%2x:%2x:%2x:%2x:%2x:%2x", &a[0], &a[1], &a[2], &a[3],
|
||||
&a[4], &a[5]);
|
||||
&a[4], &a[5]);
|
||||
if (c == 6) {
|
||||
mac->adr[0] = a[0];
|
||||
mac->adr[1] = a[1];
|
||||
@@ -301,14 +310,14 @@ static const char *Address_Cache_Filename = "address_cache";
|
||||
|
||||
static void address_file_init(const char *pFilename)
|
||||
{
|
||||
FILE *pFile = NULL; /* stream pointer */
|
||||
char line[256] = {""}; /* holds line from file */
|
||||
FILE *pFile = NULL; /* stream pointer */
|
||||
char line[256] = { "" }; /* holds line from file */
|
||||
long device_id = 0;
|
||||
unsigned snet = 0;
|
||||
unsigned max_apdu = 0;
|
||||
char mac_string[80] = {""}, sadr_string[80] = {""};
|
||||
BACNET_ADDRESS src = {0};
|
||||
BACNET_MAC_ADDRESS mac = {0};
|
||||
char mac_string[80] = { "" }, sadr_string[80] = { "" };
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
BACNET_MAC_ADDRESS mac = { 0 };
|
||||
int index = 0;
|
||||
|
||||
pFile = fopen(pFilename, "r");
|
||||
@@ -317,8 +326,8 @@ static void address_file_init(const char *pFilename)
|
||||
/* ignore comments */
|
||||
if (line[0] != ';') {
|
||||
if (sscanf(line, "%7ld %79s %5u %79s %4u", &device_id,
|
||||
&mac_string[0], &snet, &sadr_string[0],
|
||||
&max_apdu) == 5) {
|
||||
&mac_string[0], &snet, &sadr_string[0],
|
||||
&max_apdu) == 5) {
|
||||
if (address_mac_from_ascii(&mac, mac_string)) {
|
||||
src.mac_len = mac.len;
|
||||
for (index = 0; index < MAX_MAC_LEN; index++) {
|
||||
@@ -341,7 +350,7 @@ static void address_file_init(const char *pFilename)
|
||||
}
|
||||
address_add((uint32_t)device_id, max_apdu, &src);
|
||||
address_set_device_TTL((uint32_t)device_id, 0,
|
||||
true); /* Mark as static entry */
|
||||
true); /* Mark as static entry */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,8 +400,9 @@ void address_init_partial(void)
|
||||
if ((pMatch->Flags & BAC_ADDR_IN_USE) !=
|
||||
0) { /* It's in use so let's check further */
|
||||
if (((pMatch->Flags & BAC_ADDR_BIND_REQ) != 0) ||
|
||||
(pMatch->TimeToLive == 0))
|
||||
(pMatch->TimeToLive == 0)) {
|
||||
pMatch->Flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((pMatch->Flags & BAC_ADDR_RESERVED) !=
|
||||
@@ -416,8 +426,8 @@ void address_init_partial(void)
|
||||
* to avoid breaking the current API. *
|
||||
****************************************************************************/
|
||||
|
||||
void address_set_device_TTL(uint32_t device_id, uint32_t TimeOut,
|
||||
bool StaticFlag)
|
||||
void address_set_device_TTL(
|
||||
uint32_t device_id, uint32_t TimeOut, bool StaticFlag)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
|
||||
@@ -435,8 +445,8 @@ void address_set_device_TTL(uint32_t device_id, uint32_t TimeOut,
|
||||
pMatch->TimeToLive = TimeOut;
|
||||
}
|
||||
} else {
|
||||
pMatch->TimeToLive =
|
||||
TimeOut; /* For unbound we can only set the time to live */
|
||||
pMatch->TimeToLive = TimeOut; /* For unbound we can only set the
|
||||
time to live */
|
||||
}
|
||||
break; /* Exit now if found at all - bound or unbound */
|
||||
}
|
||||
@@ -444,8 +454,8 @@ void address_set_device_TTL(uint32_t device_id, uint32_t TimeOut,
|
||||
}
|
||||
}
|
||||
|
||||
bool address_get_by_device(uint32_t device_id, unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
bool address_get_by_device(
|
||||
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
bool found = false; /* return value */
|
||||
@@ -519,20 +529,21 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
/* Pick the right time to live */
|
||||
|
||||
if ((pMatch->Flags & BAC_ADDR_BIND_REQ) !=
|
||||
0) /* Bind requested so long time */
|
||||
0) { /* Bind requested so long time */
|
||||
pMatch->TimeToLive = BAC_ADDR_LONG_TIME;
|
||||
else if ((pMatch->Flags & BAC_ADDR_STATIC) !=
|
||||
0) /* Static already so make sure it never expires */
|
||||
} else if ((pMatch->Flags & BAC_ADDR_STATIC) !=
|
||||
0) { /* Static already so make sure it never expires */
|
||||
pMatch->TimeToLive = BAC_ADDR_FOREVER;
|
||||
else if ((pMatch->Flags & BAC_ADDR_SHORT_TTL) !=
|
||||
0) /* Opportunistic entry so leave on short fuse */
|
||||
} else if ((pMatch->Flags & BAC_ADDR_SHORT_TTL) !=
|
||||
0) { /* Opportunistic entry so leave on short fuse */
|
||||
pMatch->TimeToLive = BAC_ADDR_SHORT_TIME;
|
||||
else
|
||||
} else {
|
||||
pMatch->TimeToLive =
|
||||
BAC_ADDR_LONG_TIME; /* Renewing existing entry */
|
||||
}
|
||||
|
||||
pMatch->Flags &=
|
||||
~BAC_ADDR_BIND_REQ; /* Clear bind request flag just in case */
|
||||
pMatch->Flags &= ~BAC_ADDR_BIND_REQ; /* Clear bind request flag just
|
||||
in case */
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -575,8 +586,10 @@ void address_add(uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
|
||||
/* returns true if device is already bound */
|
||||
/* also returns the address and max apdu if already bound */
|
||||
bool address_device_bind_request(uint32_t device_id, uint32_t *device_ttl,
|
||||
unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
bool address_device_bind_request(uint32_t device_id,
|
||||
uint32_t *device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
{
|
||||
bool found = false; /* return value */
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
@@ -602,7 +615,8 @@ bool address_device_bind_request(uint32_t device_id, uint32_t *device_ttl,
|
||||
pMatch->Flags &=
|
||||
~BAC_ADDR_SHORT_TTL; /* Convert to normal entry */
|
||||
pMatch->TimeToLive =
|
||||
BAC_ADDR_LONG_TIME; /* And give it a decent time to live
|
||||
BAC_ADDR_LONG_TIME; /* And give it a decent time to
|
||||
* live
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -641,14 +655,14 @@ bool address_device_bind_request(uint32_t device_id, uint32_t *device_ttl,
|
||||
|
||||
/* returns true if device is already bound */
|
||||
/* also returns the address and max apdu if already bound */
|
||||
bool address_bind_request(uint32_t device_id, unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
bool address_bind_request(
|
||||
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
return address_device_bind_request(device_id, NULL, max_apdu, src);
|
||||
}
|
||||
|
||||
void address_add_binding(uint32_t device_id, unsigned max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
void address_add_binding(
|
||||
uint32_t device_id, unsigned max_apdu, BACNET_ADDRESS *src)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
|
||||
@@ -673,9 +687,11 @@ void address_add_binding(uint32_t device_id, unsigned max_apdu,
|
||||
return;
|
||||
}
|
||||
|
||||
bool address_device_get_by_index(unsigned index, uint32_t *device_id,
|
||||
uint32_t *device_ttl, unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
bool address_device_get_by_index(unsigned index,
|
||||
uint32_t *device_id,
|
||||
uint32_t *device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
{
|
||||
struct Address_Cache_Entry *pMatch;
|
||||
bool found = false; /* return value */
|
||||
@@ -703,8 +719,10 @@ bool address_device_get_by_index(unsigned index, uint32_t *device_id,
|
||||
return found;
|
||||
}
|
||||
|
||||
bool address_get_by_index(unsigned index, uint32_t *device_id,
|
||||
unsigned *max_apdu, BACNET_ADDRESS *src)
|
||||
bool address_get_by_index(unsigned index,
|
||||
uint32_t *device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS *src)
|
||||
{
|
||||
return address_device_get_by_index(index, device_id, NULL, max_apdu, src);
|
||||
}
|
||||
@@ -718,8 +736,9 @@ unsigned address_count(void)
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
/* Only count bound entries */
|
||||
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) ==
|
||||
BAC_ADDR_IN_USE)
|
||||
BAC_ADDR_IN_USE) {
|
||||
count++;
|
||||
}
|
||||
|
||||
pMatch++;
|
||||
}
|
||||
@@ -747,21 +766,21 @@ int address_list_encode(uint8_t *apdu, unsigned apdu_len)
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
if ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) ==
|
||||
BAC_ADDR_IN_USE) {
|
||||
iLen += encode_application_object_id(&apdu[iLen], OBJECT_DEVICE,
|
||||
pMatch->device_id);
|
||||
iLen += encode_application_object_id(
|
||||
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
|
||||
iLen +=
|
||||
encode_application_unsigned(&apdu[iLen], pMatch->address.net);
|
||||
|
||||
/* pick the appropriate type of entry from the cache */
|
||||
|
||||
if (pMatch->address.len != 0) {
|
||||
octetstring_init(&MAC_Address, pMatch->address.adr,
|
||||
pMatch->address.len);
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.adr, pMatch->address.len);
|
||||
iLen +=
|
||||
encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
} else {
|
||||
octetstring_init(&MAC_Address, pMatch->address.mac,
|
||||
pMatch->address.mac_len);
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.mac, pMatch->address.mac_len);
|
||||
iLen +=
|
||||
encode_application_octet_string(&apdu[iLen], &MAC_Address);
|
||||
}
|
||||
@@ -803,11 +822,11 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
int32_t iTemp = 0;
|
||||
struct Address_Cache_Entry *pMatch = NULL;
|
||||
BACNET_OCTET_STRING MAC_Address;
|
||||
uint32_t uiTotal = 0; /* Number of bound entries in the cache */
|
||||
uint32_t uiIndex = 0; /* Current entry number */
|
||||
uint32_t uiFirst = 0; /* Entry number we started encoding from */
|
||||
uint32_t uiLast = 0; /* Entry number we finished encoding on */
|
||||
uint32_t uiTarget = 0; /* Last entry we are required to encode */
|
||||
uint32_t uiTotal = 0; /* Number of bound entries in the cache */
|
||||
uint32_t uiIndex = 0; /* Current entry number */
|
||||
uint32_t uiFirst = 0; /* Entry number we started encoding from */
|
||||
uint32_t uiLast = 0; /* Entry number we finished encoding on */
|
||||
uint32_t uiTarget = 0; /* Last entry we are required to encode */
|
||||
uint32_t uiRemaining = 0; /* Amount of unused space in packet */
|
||||
|
||||
/* Initialise result flags to all false */
|
||||
@@ -818,10 +837,11 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
/* See how much space we have */
|
||||
uiRemaining = (uint32_t)(MAX_APDU - pRequest->Overhead);
|
||||
|
||||
pRequest->ItemCount = 0; /* Start out with nothing */
|
||||
pRequest->ItemCount = 0; /* Start out with nothing */
|
||||
uiTotal = address_count(); /* What do we have to work with here ? */
|
||||
if (uiTotal == 0) /* Bail out now if nowt */
|
||||
if (uiTotal == 0) { /* Bail out now if nowt */
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (pRequest->RequestType == RR_READ_ALL) {
|
||||
/*
|
||||
@@ -829,7 +849,7 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
* a range that covers the whole list and falling through to the next
|
||||
* section of code
|
||||
*/
|
||||
pRequest->Count = uiTotal; /* Full list */
|
||||
pRequest->Count = uiTotal; /* Full list */
|
||||
pRequest->Range.RefIndex = 1; /* Starting at the beginning */
|
||||
}
|
||||
|
||||
@@ -864,19 +884,22 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
/* From here on in we only have a starting point and a positive count */
|
||||
|
||||
if (pRequest->Range.RefIndex >
|
||||
uiTotal) /* Nothing to return as we are past the end of the list */
|
||||
uiTotal) { /* Nothing to return as we are past the end of the list */
|
||||
return (0);
|
||||
}
|
||||
|
||||
uiTarget = pRequest->Range.RefIndex + pRequest->Count -
|
||||
1; /* Index of last required entry */
|
||||
if (uiTarget > uiTotal) /* Capped at end of list if necessary */
|
||||
1; /* Index of last required entry */
|
||||
if (uiTarget > uiTotal) { /* Capped at end of list if necessary */
|
||||
uiTarget = uiTotal;
|
||||
}
|
||||
|
||||
pMatch = Address_Cache;
|
||||
uiIndex = 1;
|
||||
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
|
||||
BAC_ADDR_IN_USE) /* Find first bound entry */
|
||||
BAC_ADDR_IN_USE) { /* Find first bound entry */
|
||||
pMatch++;
|
||||
}
|
||||
|
||||
/* Seek to start position */
|
||||
while (uiIndex != pRequest->Range.RefIndex) {
|
||||
@@ -884,8 +907,9 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
BAC_ADDR_IN_USE) { /* Only count bound entries */
|
||||
pMatch++;
|
||||
uiIndex++;
|
||||
} else
|
||||
} else {
|
||||
pMatch++;
|
||||
}
|
||||
}
|
||||
|
||||
uiFirst = uiIndex; /* Record where we started from */
|
||||
@@ -895,49 +919,52 @@ int rr_address_list_encode(uint8_t *apdu, BACNET_READ_RANGE_DATA *pRequest)
|
||||
* Can't fit any more in! We just set the result flag to say there
|
||||
* was more and drop out of the loop early
|
||||
*/
|
||||
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS,
|
||||
true);
|
||||
bitstring_set_bit(
|
||||
&pRequest->ResultFlags, RESULT_FLAG_MORE_ITEMS, true);
|
||||
break;
|
||||
}
|
||||
|
||||
iTemp = (int32_t)encode_application_object_id(
|
||||
&apdu[iLen], OBJECT_DEVICE, pMatch->device_id);
|
||||
iTemp += encode_application_unsigned(&apdu[iLen + iTemp],
|
||||
pMatch->address.net);
|
||||
iTemp += encode_application_unsigned(
|
||||
&apdu[iLen + iTemp], pMatch->address.net);
|
||||
|
||||
/* pick the appropriate type of entry from the cache */
|
||||
|
||||
if (pMatch->address.len != 0) {
|
||||
octetstring_init(&MAC_Address, pMatch->address.adr,
|
||||
pMatch->address.len);
|
||||
iTemp += encode_application_octet_string(&apdu[iLen + iTemp],
|
||||
&MAC_Address);
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.adr, pMatch->address.len);
|
||||
iTemp += encode_application_octet_string(
|
||||
&apdu[iLen + iTemp], &MAC_Address);
|
||||
} else {
|
||||
octetstring_init(&MAC_Address, pMatch->address.mac,
|
||||
pMatch->address.mac_len);
|
||||
iTemp += encode_application_octet_string(&apdu[iLen + iTemp],
|
||||
&MAC_Address);
|
||||
octetstring_init(
|
||||
&MAC_Address, pMatch->address.mac, pMatch->address.mac_len);
|
||||
iTemp += encode_application_octet_string(
|
||||
&apdu[iLen + iTemp], &MAC_Address);
|
||||
}
|
||||
|
||||
uiRemaining -= iTemp; /* Reduce the remaining space */
|
||||
iLen += iTemp; /* and increase the length consumed */
|
||||
iLen += iTemp; /* and increase the length consumed */
|
||||
|
||||
uiLast = uiIndex; /* Record the last entry encoded */
|
||||
uiIndex++; /* and get ready for next one */
|
||||
uiIndex++; /* and get ready for next one */
|
||||
pMatch++;
|
||||
pRequest->ItemCount++; /* Chalk up another one for the response count */
|
||||
|
||||
while ((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_BIND_REQ)) !=
|
||||
BAC_ADDR_IN_USE) /* Find next bound entry */
|
||||
BAC_ADDR_IN_USE) { /* Find next bound entry */
|
||||
pMatch++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set remaining result flags if necessary */
|
||||
if (uiFirst == 1)
|
||||
if (uiFirst == 1) {
|
||||
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_FIRST_ITEM, true);
|
||||
}
|
||||
|
||||
if (uiLast == uiTotal)
|
||||
if (uiLast == uiTotal) {
|
||||
bitstring_set_bit(&pRequest->ResultFlags, RESULT_FLAG_LAST_ITEM, true);
|
||||
}
|
||||
|
||||
return (iLen);
|
||||
}
|
||||
@@ -957,11 +984,13 @@ void address_cache_timer(uint16_t uSeconds)
|
||||
while (pMatch <= &Address_Cache[MAX_ADDRESS_CACHE - 1]) {
|
||||
if (((pMatch->Flags & (BAC_ADDR_IN_USE | BAC_ADDR_RESERVED)) != 0) &&
|
||||
((pMatch->Flags & BAC_ADDR_STATIC) ==
|
||||
0)) { /* Check all entries holding a slot except statics */
|
||||
if (pMatch->TimeToLive >= uSeconds)
|
||||
0)) { /* Check all entries holding a slot except statics
|
||||
*/
|
||||
if (pMatch->TimeToLive >= uSeconds) {
|
||||
pMatch->TimeToLive -= uSeconds;
|
||||
else
|
||||
} else {
|
||||
pMatch->Flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pMatch++;
|
||||
@@ -988,8 +1017,10 @@ static void set_address(unsigned index, BACNET_ADDRESS *dest)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_file_address(const char *pFilename, uint32_t device_id,
|
||||
BACNET_ADDRESS *dest, uint16_t max_apdu)
|
||||
static void set_file_address(const char *pFilename,
|
||||
uint32_t device_id,
|
||||
BACNET_ADDRESS *dest,
|
||||
uint16_t max_apdu)
|
||||
{
|
||||
unsigned i;
|
||||
FILE *pFile = NULL;
|
||||
@@ -1023,10 +1054,10 @@ static void set_file_address(const char *pFilename, uint32_t device_id,
|
||||
#ifdef BACNET_ADDRESS_CACHE_FILE
|
||||
void testAddressFile(Test *pTest)
|
||||
{
|
||||
BACNET_ADDRESS src = {0};
|
||||
BACNET_ADDRESS src = { 0 };
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 480;
|
||||
BACNET_ADDRESS test_address = {0};
|
||||
BACNET_ADDRESS test_address = { 0 };
|
||||
unsigned test_max_apdu = 0;
|
||||
|
||||
/* create a fake address */
|
||||
@@ -1039,8 +1070,8 @@ void testAddressFile(Test *pTest)
|
||||
set_file_address(Address_Cache_Filename, device_id, &src, max_apdu);
|
||||
/* retrieve it from the file, and see if we can find it */
|
||||
address_file_init(Address_Cache_Filename);
|
||||
ct_test(pTest,
|
||||
address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
ct_test(
|
||||
pTest, address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
ct_test(pTest, test_max_apdu == max_apdu);
|
||||
ct_test(pTest, bacnet_address_same(&test_address, &src));
|
||||
|
||||
@@ -1060,8 +1091,8 @@ void testAddressFile(Test *pTest)
|
||||
set_file_address(Address_Cache_Filename, device_id, &src, max_apdu);
|
||||
/* retrieve it from the file, and see if we can find it */
|
||||
address_file_init(Address_Cache_Filename);
|
||||
ct_test(pTest,
|
||||
address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
ct_test(
|
||||
pTest, address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
ct_test(pTest, test_max_apdu == max_apdu);
|
||||
ct_test(pTest, bacnet_address_same(&test_address, &src));
|
||||
}
|
||||
@@ -1090,12 +1121,13 @@ void testAddress(Test *pTest)
|
||||
device_id = i * 255;
|
||||
set_address(i, &src);
|
||||
/* test the lookup by device id */
|
||||
ct_test(pTest, address_get_by_device(device_id, &test_max_apdu,
|
||||
&test_address));
|
||||
ct_test(pTest,
|
||||
address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
ct_test(pTest, test_max_apdu == max_apdu);
|
||||
ct_test(pTest, bacnet_address_same(&test_address, &src));
|
||||
ct_test(pTest, address_get_by_index(i, &test_device_id, &test_max_apdu,
|
||||
&test_address));
|
||||
ct_test(pTest,
|
||||
address_get_by_index(
|
||||
i, &test_device_id, &test_max_apdu, &test_address));
|
||||
ct_test(pTest, test_device_id == device_id);
|
||||
ct_test(pTest, test_max_apdu == max_apdu);
|
||||
ct_test(pTest, bacnet_address_same(&test_address, &src));
|
||||
@@ -1108,8 +1140,8 @@ void testAddress(Test *pTest)
|
||||
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
|
||||
device_id = i * 255;
|
||||
address_remove_device(device_id);
|
||||
ct_test(pTest, !address_get_by_device(device_id, &test_max_apdu,
|
||||
&test_address));
|
||||
ct_test(pTest,
|
||||
!address_get_by_device(device_id, &test_max_apdu, &test_address));
|
||||
count = address_count();
|
||||
ct_test(pTest, count == (MAX_ADDRESS_CACHE - i - 1));
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 ADDRESS_H
|
||||
#define ADDRESS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/readrange.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void address_init(
|
||||
void);
|
||||
|
||||
void address_init_partial(
|
||||
void);
|
||||
|
||||
void address_add(
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
void address_remove_device(
|
||||
uint32_t device_id);
|
||||
|
||||
bool address_get_by_device(
|
||||
uint32_t device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_get_by_index(
|
||||
unsigned index,
|
||||
uint32_t * device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_device_get_by_index(
|
||||
unsigned index,
|
||||
uint32_t * device_id,
|
||||
uint32_t * device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_get_device_id(
|
||||
BACNET_ADDRESS * src,
|
||||
uint32_t * device_id);
|
||||
|
||||
unsigned address_count(
|
||||
void);
|
||||
|
||||
bool address_match(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_bind_request(
|
||||
uint32_t device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_device_bind_request(
|
||||
uint32_t device_id,
|
||||
uint32_t * device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
void address_add_binding(
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
int address_list_encode(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len);
|
||||
|
||||
int rr_address_list_encode(
|
||||
uint8_t * apdu,
|
||||
BACNET_READ_RANGE_DATA * pRequest);
|
||||
|
||||
void address_set_device_TTL(
|
||||
uint32_t device_id,
|
||||
uint32_t TimeOut,
|
||||
bool StaticFlag);
|
||||
|
||||
void address_cache_timer(
|
||||
uint16_t uSeconds);
|
||||
|
||||
void address_mac_init(
|
||||
BACNET_MAC_ADDRESS *mac,
|
||||
uint8_t *adr,
|
||||
uint8_t len);
|
||||
|
||||
bool address_mac_from_ascii(
|
||||
BACNET_MAC_ADDRESS *mac,
|
||||
char *arg);
|
||||
|
||||
void address_protected_entry_index_set(uint32_t top_protected_entry_index);
|
||||
void address_own_device_id_set(uint32_t own_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 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 <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */
|
||||
|
||||
/** Handler for the NPDU portion of a received packet.
|
||||
* Aside from error-checking, if the NPDU doesn't contain routing info,
|
||||
* this handler doesn't do much besides stepping over the NPDU header
|
||||
* and passing the remaining bytes to the apdu_handler.
|
||||
* @note The routing (except src) and NCPI information, including
|
||||
* npdu_data->data_expecting_reply, are discarded.
|
||||
* @see routing_npdu_handler
|
||||
*
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void npdu_handler(BACNET_ADDRESS *src, /* source address */
|
||||
uint8_t *pdu, /* PDU data */
|
||||
uint16_t pdu_len)
|
||||
{ /* length PDU */
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (npdu_data.network_layer_message) {
|
||||
/*FIXME: network layer message received! Handle it! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "NPDU: Network Layer Message discarded!\n");
|
||||
#endif
|
||||
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
|
||||
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
|
||||
/* only handle the version that we know how to handle */
|
||||
/* and we are not a router, so ignore messages with
|
||||
routing information cause they are not for us */
|
||||
if ((dest.net == BACNET_BROADCAST_NETWORK) &&
|
||||
((pdu[apdu_offset] & 0xF0) ==
|
||||
PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
|
||||
/* hack for 5.4.5.1 - IDLE */
|
||||
/* ConfirmedBroadcastReceived */
|
||||
/* then enter IDLE - ignore the PDU */
|
||||
} else {
|
||||
apdu_handler(src, &pdu[apdu_offset],
|
||||
(uint16_t)(pdu_len - apdu_offset));
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: DNET=%u. Discarded!\n", (unsigned)dest.net);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned)pdu[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic NPDU handler
|
||||
*
|
||||
* @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 NPDU_HANDLER_H
|
||||
#define NPDU_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void npdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
void npdu_handler_cleanup(void);
|
||||
void npdu_handler_init(
|
||||
uint16_t bip_net,
|
||||
uint16_t mstp_net);
|
||||
void npdu_router_handler(
|
||||
uint16_t snet,
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
int npdu_router_send_pdu(
|
||||
uint16_t dnet,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * pdu,
|
||||
unsigned int pdu_len);
|
||||
void npdu_router_get_my_address(
|
||||
uint16_t dnet,
|
||||
BACNET_ADDRESS * my_address);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,302 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 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.
|
||||
*
|
||||
*********************************************************************/
|
||||
/* Acknowledging the contribution of code and ideas used here that
|
||||
* came from Paul Chapman's vmac demo project. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#if defined(BACDL_BIP)
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
#endif
|
||||
|
||||
/** @file h_routed_npdu.c Handles messages at the NPDU level of the BACnet
|
||||
* stack, including routing and network control messages. */
|
||||
|
||||
/** Handler to manage the Network Layer Control Messages received in a packet.
|
||||
* This handler is called if the NCPI bit 7 indicates that this packet is a
|
||||
* network layer message and there is no further DNET to pass it to.
|
||||
* The NCPI has already been decoded into the npdu_data structure.
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [in] The routing source information, if any.
|
||||
* If src->net and src->len are 0, there is no
|
||||
* routing source information.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param npdu_data [in] Contains a filled-out structure with information
|
||||
* decoded from the NCPI and other NPDU
|
||||
* bytes.
|
||||
* @param npdu [in] Buffer containing the rest of the NPDU, following the
|
||||
* bytes that have already been decoded.
|
||||
* @param npdu_len [in] The length of the remaining NPDU message in npdu[].
|
||||
*/
|
||||
static void network_control_handler(BACNET_ADDRESS *src,
|
||||
int *DNET_list,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *npdu,
|
||||
uint16_t npdu_len)
|
||||
{
|
||||
uint16_t npdu_offset = 0;
|
||||
uint16_t dnet = 0;
|
||||
uint16_t len = 0;
|
||||
|
||||
switch (npdu_data->network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
/* Send I-am-router-to-network with our one-network list if
|
||||
* our specific network is requested, or no specific
|
||||
* network is requested. Silently drop other DNET requests.
|
||||
*/
|
||||
if (npdu_len >= 2) {
|
||||
uint16_t network;
|
||||
len += decode_unsigned16(&npdu[len], &network);
|
||||
if (network == DNET_list[0]) {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
} else {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
/* Per the standard, we are supposed to process this message and
|
||||
* add its DNETs to our routing table.
|
||||
* However, since we only have one upstream port that these
|
||||
* messages can come from and replies go to, it doesn't seem
|
||||
* to provide us any value to do this; when we need to send to
|
||||
* some remote device, we will start by pushing it out the
|
||||
* upstream port and let the attached router(s) take it from there.
|
||||
* Consequently, we'll do nothing interesting here.
|
||||
* -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK
|
||||
* later for congestion control - then it could matter.
|
||||
*/
|
||||
debug_printf("%s for Networks: ",
|
||||
bactext_network_layer_msg_name(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK));
|
||||
while (npdu_len >= 2) {
|
||||
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
|
||||
debug_printf("%hu", dnet);
|
||||
npdu_len -= len;
|
||||
npdu_offset += len;
|
||||
if (npdu_len >= 2) {
|
||||
debug_printf(", ");
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
/* Do nothing, same as previous case. */
|
||||
break;
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
if (npdu_len >= 3) {
|
||||
decode_unsigned16(&npdu[1], &dnet);
|
||||
debug_printf("Received %s for Network: ",
|
||||
bactext_network_layer_msg_name(
|
||||
NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK));
|
||||
debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
/* Do nothing - don't support upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
/* If sent with Number of Ports == 0, we respond with
|
||||
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
|
||||
* reachable networks.
|
||||
*/
|
||||
if (npdu_len > 0) {
|
||||
/* If Number of Ports is 0, broadcast our "full" table */
|
||||
if (npdu[0] == 0) {
|
||||
Send_Initialize_Routing_Table_Ack(NULL, DNET_list);
|
||||
} else {
|
||||
/* If they sent us a list, just politely ACK it
|
||||
* with no routing list of our own. But we don't DO
|
||||
* anything with the info, either.
|
||||
*/
|
||||
int listTerminator = -1;
|
||||
Send_Initialize_Routing_Table_Ack(src, &listTerminator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Else, fall through to do nothing. */
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* Do nothing with the routing table info, since don't support
|
||||
* upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
/* Do nothing - don't support PTP half-router control */
|
||||
break;
|
||||
default:
|
||||
/* An unrecognized message is bad; send an error response. */
|
||||
Send_Reject_Message_To_Network(
|
||||
src, NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]);
|
||||
/* Sending our DNET doesn't make a lot of sense, does it? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** An APDU pre-handler that makes sure that the subsequent APDU handler call
|
||||
* operates on the right Device Object(s), as addressed by the destination
|
||||
* (routing) information.
|
||||
*
|
||||
* @note Even when the destination is "routed" to our virtual BACnet network,
|
||||
* the src information does not need to change to reflect that (as it normally
|
||||
* would for a routed message) because the reply will be sent from the level
|
||||
* of the gateway Device.
|
||||
*
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
* @param dest [in] The BACNET_ADDRESS of the message's destination.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param apdu [in] The apdu portion of the request, to be processed.
|
||||
* @param apdu_len [in] The total (remaining) length of the apdu.
|
||||
*/
|
||||
static void routed_apdu_handler(BACNET_ADDRESS *src,
|
||||
BACNET_ADDRESS *dest,
|
||||
int *DNET_list,
|
||||
uint8_t *apdu,
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
int cursor = 0; /* Starting hint */
|
||||
bool bGotOne = false;
|
||||
|
||||
if (!Routed_Device_Is_Valid_Network(dest->net, DNET_list)) {
|
||||
/* We don't know how to reach this one.
|
||||
* The protocol doesn't specifically state this, but if this message
|
||||
* was broadcast to us, we should assume "someone else" is handling
|
||||
* it and not get involved (ie, send a Reject-Message).
|
||||
* Since we can't reach other routers that src couldn't already reach,
|
||||
* we don't try the standard path of asking Who-Is-Router-to-Network. */
|
||||
#if defined(BACDL_BIP)
|
||||
/* If wasn't unicast to us, must have been one of the bcast types.
|
||||
* Drop it. */
|
||||
if (bvlc_get_function_code() != BVLC_ORIGINAL_UNICAST_NPDU) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Upper level handlers knew that this was sent as a bcast,
|
||||
* but our only other way to guess at that here is if the dest->adr
|
||||
* is absent, then we know this is some sort of bcast.
|
||||
*/
|
||||
if (dest->len > 0) {
|
||||
Send_Reject_Message_To_Network(
|
||||
src, NETWORK_REJECT_NO_ROUTE, dest->net);
|
||||
} /* else, silently drop it */
|
||||
return;
|
||||
}
|
||||
|
||||
while (Routed_Device_GetNext(dest, DNET_list, &cursor)) {
|
||||
apdu_handler(src, apdu, apdu_len);
|
||||
bGotOne = true;
|
||||
if (cursor < 0) { /* If no more matches, */
|
||||
break; /* We don't need to keep looking */
|
||||
}
|
||||
}
|
||||
if (!bGotOne) {
|
||||
/* Just silently drop this packet. */
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for the NPDU portion of a received packet, which may have routing.
|
||||
* This is a fuller handler than the regular npdu_handler, as it manages
|
||||
* - Decoding of the NCPI byte
|
||||
* - Further processing by network_control_handler() if this is a network
|
||||
* layer message.
|
||||
* - Further processing by routed_apdu_handler() if it contains an APDU
|
||||
* - Normally (no routing) by apdu_handler()
|
||||
* - With Routing (a further destination was indicated) by the decoded
|
||||
* destination.
|
||||
* - Errors in decoding.
|
||||
* @note The npdu_data->data_expecting_reply status is discarded.
|
||||
* @see npdu_handler
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void routing_npdu_handler(
|
||||
BACNET_ADDRESS *src, int *DNET_list, uint8_t *pdu, uint16_t pdu_len)
|
||||
{
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (apdu_offset <= 0) {
|
||||
debug_printf("NPDU: Decoding failed; Discarded!\n");
|
||||
} else if (npdu_data.network_layer_message) {
|
||||
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
|
||||
network_control_handler(src, DNET_list, &npdu_data,
|
||||
&pdu[apdu_offset], (uint16_t)(pdu_len - apdu_offset));
|
||||
} else {
|
||||
/* The DNET is set, but we don't support downstream routers,
|
||||
* so we just silently drop this network layer message,
|
||||
* since only routers can handle it (even if for our DNET) */
|
||||
}
|
||||
} else if (apdu_offset <= pdu_len) {
|
||||
if ((dest.net == 0) || (npdu_data.hop_count > 1)) {
|
||||
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
|
||||
(uint16_t)(pdu_len - apdu_offset));
|
||||
}
|
||||
/* Else, hop_count bottomed out and we discard this one. */
|
||||
}
|
||||
} else {
|
||||
/* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */
|
||||
debug_printf(
|
||||
"NPDU: Unsupported BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned)pdu[0]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic routing NPDU handler
|
||||
*
|
||||
* @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 ROUTING_NPDU_HANDLER_H
|
||||
#define ROUTING_NPDU_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void routing_npdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
int *DNET_list,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,320 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/bactext.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
/** @file s_router.c Methods to send various BACnet Router Network Layer
|
||||
* Messages. */
|
||||
|
||||
/** Initialize an npdu_data structure with given parameters and good defaults,
|
||||
* and add the Network Layer Message fields.
|
||||
* The name is a misnomer, as it doesn't do any actual encoding here.
|
||||
* @see npdu_encode_npdu_data for a simpler version to use when sending an
|
||||
* APDU instead of a Network Layer Message.
|
||||
*
|
||||
* @param npdu_data [out] Returns a filled-out structure with information
|
||||
* provided by the other arguments and
|
||||
* good defaults.
|
||||
* @param network_message_type [in] The type of Network Layer Message.
|
||||
* @param data_expecting_reply [in] True if message should have a reply.
|
||||
* @param priority [in] One of the 4 priorities defined in section 6.2.2,
|
||||
* like B'11' = Life Safety message
|
||||
*/
|
||||
void npdu_encode_npdu_network(BACNET_NPDU_DATA *npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply,
|
||||
BACNET_MESSAGE_PRIORITY priority)
|
||||
{
|
||||
if (npdu_data) {
|
||||
npdu_data->data_expecting_reply = data_expecting_reply;
|
||||
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
|
||||
npdu_data->network_layer_message = true; /* false if APDU */
|
||||
npdu_data->network_message_type = network_message_type; /* optional */
|
||||
npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */
|
||||
npdu_data->priority = priority;
|
||||
npdu_data->hop_count = HOP_COUNT_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/** Function to encode and send any supported Network Layer Message.
|
||||
* The payload for the message is encoded from information in the iArgs[] array.
|
||||
* The contents of iArgs are are, per message type:
|
||||
* - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: Single int for DNET requested
|
||||
* - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: Array of DNET(s) to send,
|
||||
* terminated with -1
|
||||
* - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: array of 2 ints,
|
||||
* first is reason, second is DNET of interest
|
||||
* - NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_INIT_RT_TABLE and NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
* Array of DNET(s) to process as "Ports", terminated with -1. Each DNET
|
||||
* will be expanded to a BACNET_ROUTER_PORT (with simple defaults for
|
||||
* most fields) and encoded.
|
||||
*
|
||||
* @param network_message_type [in] The type of message to be sent.
|
||||
* @param dst [in/out] If not NULL, contains the destination for the message.
|
||||
* @param iArgs [in] An optional array of values whose meaning depends on
|
||||
* the type of message.
|
||||
* @return Number of bytes sent, or <=0 if no message was sent.
|
||||
*/
|
||||
int Send_Network_Layer_Message(BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
BACNET_ADDRESS *dst,
|
||||
int *iArgs)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
int *pVal = iArgs; /* Start with first value */
|
||||
bool data_expecting_reply = false;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS bcastDest;
|
||||
|
||||
if (iArgs == NULL) {
|
||||
return 0; /* Can't do anything here */
|
||||
}
|
||||
|
||||
/* If dst was NULL, get our (local net) broadcast MAC address. */
|
||||
if (dst == NULL) {
|
||||
datalink_get_broadcast_address(&bcastDest);
|
||||
dst = &bcastDest;
|
||||
}
|
||||
|
||||
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE) {
|
||||
data_expecting_reply = true; /* DER in this one case */
|
||||
}
|
||||
npdu_encode_npdu_network(&npdu_data, network_message_type,
|
||||
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
/* We don't need src information, since a message can't originate from
|
||||
* our downstream BACnet network.
|
||||
*/
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dst, NULL, &npdu_data);
|
||||
|
||||
/* Now encode the optional payload bytes, per message type */
|
||||
switch (network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
if (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
}
|
||||
/* else, don't encode a DNET */
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
while (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
pVal++;
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
/* Encode the Reason byte, then the DNET */
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t)*pVal;
|
||||
pVal++;
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* First, count the number of Ports we will encode */
|
||||
len = 0; /* Re-purpose len as our counter here */
|
||||
while (*pVal >= 0) {
|
||||
len++;
|
||||
pVal++;
|
||||
}
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t)len;
|
||||
|
||||
if (len > 0) {
|
||||
uint8_t portID = 1;
|
||||
pVal = iArgs; /* Reset to beginning */
|
||||
/* Now encode each (virtual) BACNET_ROUTER_PORT.
|
||||
* We will simply use a positive index for PortID,
|
||||
* and have no PortInfo.
|
||||
*/
|
||||
while (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
Handler_Transmit_Buffer[pdu_len++] = portID++;
|
||||
Handler_Transmit_Buffer[pdu_len++] = 0;
|
||||
debug_printf(
|
||||
" Sending Routing Table entry for %u \n", *pVal);
|
||||
pVal++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debug_printf("Not sent: %s message unsupported \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
return 0;
|
||||
break; /* Will never reach this line */
|
||||
}
|
||||
|
||||
if (dst != NULL) {
|
||||
debug_printf("Sending %s message to BACnet network %u \n",
|
||||
bactext_network_layer_msg_name(network_message_type), dst->net);
|
||||
} else {
|
||||
debug_printf("Sending %s message to local BACnet network \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
}
|
||||
|
||||
/* Now send the message */
|
||||
bytes_sent = datalink_send_pdu(
|
||||
dst, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
int wasErrno = errno; /* preserve the errno */
|
||||
debug_printf("Failed to send %s message (%s)!\n",
|
||||
bactext_network_layer_msg_name(network_message_type),
|
||||
strerror(wasErrno));
|
||||
}
|
||||
#endif
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Optionally may designate a particular router
|
||||
* destination to respond.
|
||||
* @param dnet [in] Which BACnet network to request for; if -1, no DNET
|
||||
* will be sent and the receiving router(s) will send
|
||||
* their full list of reachable BACnet networks.
|
||||
*/
|
||||
void Send_Who_Is_Router_To_Network(BACNET_ADDRESS *dst, int dnet)
|
||||
{
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, dst, &dnet);
|
||||
}
|
||||
|
||||
/** Broadcast an I-am-router-to-network message, giving the list of networks
|
||||
* we can reach.
|
||||
* The message will be sent to our normal DataLink Layer interface,
|
||||
* not the routed backend.
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1
|
||||
*/
|
||||
void Send_I_Am_Router_To_Network(const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, NULL, (int *)DNET_list);
|
||||
}
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Otherwise, designates a particular router
|
||||
* destination.
|
||||
* @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes.
|
||||
* @param dnet [in] Which BACnet network orginated the message.
|
||||
*/
|
||||
void Send_Reject_Message_To_Network(
|
||||
BACNET_ADDRESS *dst, uint8_t reject_reason, int dnet)
|
||||
{
|
||||
int iArgs[2];
|
||||
iArgs[0] = reject_reason;
|
||||
iArgs[1] = dnet;
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, dst, iArgs);
|
||||
debug_printf(" Reject Reason=%d, DNET=%u\n", reject_reason, dnet);
|
||||
}
|
||||
|
||||
/** Send an Initialize Routing Table message, built from an optional DNET[]
|
||||
* array.
|
||||
* There are two cases here:
|
||||
* 1) We are requesting a destination router's Routing Table.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* 2) We are sending out our Routing Table for some reason (normally bcast it).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, msg will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when requesting a Routing Table.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. Will be just -1 when we are
|
||||
* requesting a routing table.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table(BACNET_ADDRESS *dst, const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE, dst, (int *)DNET_list);
|
||||
}
|
||||
|
||||
/** Sends our Routing Table, built from our DNET[] array, as an ACK.
|
||||
* There are two cases here:
|
||||
* 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table.
|
||||
* We will normally broadcast that response.
|
||||
* 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a
|
||||
* routing table, and then we will want to respond to that dst router.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, Ack will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when ACKing receipt of this message type.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. May be just -1 when no table
|
||||
* should be sent.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table_Ack(
|
||||
BACNET_ADDRESS *dst, const int DNET_list[])
|
||||
{
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, dst, (int *)DNET_list);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic WritePropertyMultiple service send
|
||||
*
|
||||
* @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 SEND_ROUTER_H
|
||||
#define SEND_ROUTER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void npdu_encode_npdu_network(
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply,
|
||||
BACNET_MESSAGE_PRIORITY priority);
|
||||
int Send_Network_Layer_Message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
BACNET_ADDRESS * dst,
|
||||
int *iArgs);
|
||||
void Send_Who_Is_Router_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
int dnet);
|
||||
void Send_I_Am_Router_To_Network(
|
||||
const int DNET_list[]);
|
||||
void Send_Reject_Message_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
uint8_t reject_reason,
|
||||
int dnet);
|
||||
void Send_Initialize_Routing_Table(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[]);
|
||||
void Send_Initialize_Routing_Table_Ack(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[]);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
@@ -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
@@ -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 */
|
||||
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user