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

* moving folders and files and adjust server demo build

* Fix Makefile for apps/server on Linux

* fix unit test source file folders

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

* added some ignore extensions

* disable parallel make option

* fix build for abort, dcc, and epics apps

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

* Fixed building of all apps

* fix the ipv4 to ipv6 router app build

* Change indent style from Google to Webkit

* make pretty to re-format style

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

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

* remove mercurial EOL and ignore files for git repo

* remove .vscodeconfig files from repo

* tweak clang-format style

* clang-format src and apps with tweaked style

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

* result of make tidy for src and apps

* fix clang-tidy mangling

* Added code::blocks project for BACnet server simulation

* added code::blocks linux project for WhoIs app

* update text files for EOL

* fix EOL in some files

* fixed make win32 apps for older gcc

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

* created codeblocks project for apps/epics for Windows

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

* fix ports/xplained example for Atmel Studio compile

* fix ports/stm32f10x example for gcc Makefile compile

* fix ports/stm32f10x example for IAR EWARM compile

* fix ports/xplained timer callback

* fix ports/bdk_atxx_mspt build with subdirs

* fix ports/bdk_atxx_mspt build with subdirs

* updated git ignore for IAR build artifacts

* updated gitignore for non-tracked files and folders

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

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

* fixed atmega168 port for IAR AVR and GCC AVR Makefile

* fixed at91sam7s port for IAR ARM and GCC ARM Makefile

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

* changed arm7 to uip port
This commit is contained in:
Steve Karg
2019-12-13 15:19:10 -06:00
committed by GitHub
parent 8a38dbe2cf
commit d50c190957
912 changed files with 36206 additions and 52502 deletions
+50
View File
@@ -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
+36 -30
View File
@@ -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... */
+66
View File
@@ -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
+47 -35
View File
@@ -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;
+76
View File
@@ -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
+74 -58
View File
@@ -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
+86
View File
@@ -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_ */
+51
View File
@@ -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
View File
@@ -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;
+110
View File
@@ -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;
+62
View File
@@ -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;
+64
View File
@@ -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;
+60
View File
@@ -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
View File
@@ -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;
+105
View File
@@ -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
+22 -14
View File
@@ -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;
+46
View File
@@ -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
File diff suppressed because it is too large Load Diff
+256
View File
@@ -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
+194 -179
View File
@@ -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));
+444
View File
@@ -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
+180
View File
@@ -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)
+101
View File
@@ -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
+2352
View File
File diff suppressed because it is too large Load Diff
+60 -54
View File
@@ -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);
+73
View File
@@ -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
+18 -15
View File
@@ -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;
+99
View File
@@ -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
+23 -22
View File
@@ -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;
+52
View File
@@ -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:
+90
View File
@@ -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
+11 -11
View File
@@ -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]);
+79
View File
@@ -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
+92 -86
View File
@@ -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);
+207
View File
@@ -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
+1413
View File
File diff suppressed because it is too large Load Diff
+145
View File
@@ -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;
}
+62
View File
@@ -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
+62
View File
@@ -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
+4 -4
View File
@@ -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 */
+52
View File
@@ -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));
}
+127
View File
@@ -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
+110
View File
@@ -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;
}
+70
View File
@@ -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
+302
View File
@@ -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;
}
+52
View File
@@ -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
+320
View File
@@ -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);
}
+73
View File
@@ -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
+146
View File
@@ -0,0 +1,146 @@
# Unit tests for the BACnet Stack project
LOGFILE = test.log
all: ai ao av bi bo bv csv lc lo lsp \
mso msv ms-input netport osv piv command \
access_credential access_door access_point access_rights \
access_user access_zone credential_data_input
clean: logfile
rm ${LOGFILE}
logfile:
touch ${LOGFILE}
report:
cat ${LOGFILE}
access_credential: logfile access_credential.mak
$(MAKE) -s -f access_credential.mak clean all
( ./access_credential >> ${LOGFILE} )
$(MAKE) -s -f access_credential.mak clean
access_door: logfile access_door.mak
$(MAKE) -s -f access_door.mak clean all
( ./access_door >> ${LOGFILE} )
$(MAKE) -s -f access_door.mak clean
access_point: logfile access_point.mak
$(MAKE) -s -f access_point.mak clean all
( ./access_point >> ${LOGFILE} )
$(MAKE) -s -f access_point.mak clean
access_rights: logfile access_rights.mak
$(MAKE) -s -f access_rights.mak clean all
( ./access_rights >> ${LOGFILE} )
$(MAKE) -s -f access_rights.mak clean
access_user: logfile access_user.mak
$(MAKE) -s -f access_user.mak clean all
( ./access_user >> ${LOGFILE} )
$(MAKE) -s -f access_user.mak clean
access_zone: logfile access_zone.mak
$(MAKE) -s -f access_zone.mak clean all
( ./access_zone >> ${LOGFILE} )
$(MAKE) -s -f access_zone.mak clean
credential_data_input: logfile credential_data_input.mak
$(MAKE) -s -f credential_data_input.mak clean all
( ./credential_data_input >> ${LOGFILE} )
$(MAKE) -s -f credential_data_input.mak clean
ai: logfile ai.mak
$(MAKE) -s -f ai.mak clean all
( ./analog_input >> ${LOGFILE} )
$(MAKE) -s -f ai.mak clean
ao: logfile ao.mak
$(MAKE) -s -f ao.mak clean all
( ./analog_output >> ${LOGFILE} )
$(MAKE) -s -f ao.mak clean
av: logfile av.mak
$(MAKE) -s -f av.mak clean all
( ./analog_value >> ${LOGFILE} )
$(MAKE) -s -f av.mak clean
bi: logfile bi.mak
$(MAKE) -s -f bi.mak clean all
$(MAKE) -s -f bi.mak clean
bo: logfile bo.mak
$(MAKE) -s -f bo.mak clean all
( ./binary_output >> ${LOGFILE} )
$(MAKE) -s -f bo.mak clean
bv: logfile bv.mak
$(MAKE) -s -f bv.mak clean all
( ./binary_value >> ${LOGFILE} )
$(MAKE) -s -f bv.mak clean
command: logfile command.mak
$(MAKE) -s -f command.mak clean all
( ./command >> ${LOGFILE} )
$(MAKE) -s -f command.mak clean
csv: logfile csv.mak
$(MAKE) -s -f csv.mak clean all
( ./characterstring_value >> ${LOGFILE} )
$(MAKE) -s -f csv.mak clean
device: logfile device.mak
$(MAKE) -s -f device.mak clean all
( ./device >> ${LOGFILE} )
$(MAKE) -s -f device.mak clean
lc: logfile lc.mak
$(MAKE) -s -f lc.mak clean all
( ./load_control >> ${LOGFILE} )
$(MAKE) -s -f lc.mak clean
lo: logfile lo.mak
$(MAKE) -s -f lo.mak clean all
( ./lighting_output >> ${LOGFILE} )
$(MAKE) -s -f lo.mak clean
lsp: logfile lsp.mak
$(MAKE) -s -f lsp.mak clean all
( ./life_safety_point >> ${LOGFILE} )
$(MAKE) -s -f lsp.mak clean
ms-input: logfile ms-input.mak
$(MAKE) -s -f ms-input.mak clean all
( ./multistate_input >> ${LOGFILE} )
$(MAKE) -s -f ms-input.mak clean
mso: logfile mso.mak
$(MAKE) -s -f mso.mak clean all
( ./multistate_output >> ${LOGFILE} )
$(MAKE) -s -f mso.mak clean
msv: logfile msv.mak
$(MAKE) -s -f msv.mak clean all
( ./multistate_value >> ${LOGFILE} )
$(MAKE) -s -f msv.mak clean
osv: logfile osv.mak
$(MAKE) -s -f osv.mak clean all
( ./octetstring_value >> ${LOGFILE} )
$(MAKE) -s -f osv.mak clean
netport: logfile netport.mak
$(MAKE) -s -f netport.mak clean all
( ./network_port >> ${LOGFILE} )
$(MAKE) -s -f netport.mak clean
piv: logfile piv.mak
$(MAKE) -s -f piv.mak clean all
( ./positiveinteger_value >> ${LOGFILE} )
$(MAKE) -s -f piv.mak clean
schedule: logfile schedule.mak
$(MAKE) -s -f schedule.mak clean all
( ./schedule >> ${LOGFILE} )
$(MAKE) -s -f schedule.mak clean
+442
View File
@@ -0,0 +1,442 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the credential to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access Credential Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/basic/object/access_credential.h"
#include "bacnet/basic/services.h"
static bool Access_Credential_Initialized = false;
static ACCESS_CREDENTIAL_DESCR ac_descr[MAX_ACCESS_CREDENTIALS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_CREDENTIAL_STATUS,
PROP_REASON_FOR_DISABLE, PROP_AUTHENTICATION_FACTORS, PROP_ACTIVATION_TIME,
PROP_EXPIRATION_TIME, PROP_CREDENTIAL_DISABLE, PROP_ASSIGNED_ACCESS_RIGHTS,
-1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Access_Credential_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_Credential_Init(void)
{
unsigned i;
if (!Access_Credential_Initialized) {
Access_Credential_Initialized = true;
for (i = 0; i < MAX_ACCESS_CREDENTIALS; i++) {
ac_descr[i].global_identifier =
0; /* set to some meaningful value */
ac_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
ac_descr[i].credential_status = false;
ac_descr[i].reasons_count = 0;
ac_descr[i].auth_factors_count = 0;
memset(&ac_descr[i].activation_time, 0, sizeof(BACNET_DATE_TIME));
memset(&ac_descr[i].expiration_time, 0, sizeof(BACNET_DATE_TIME));
ac_descr[i].credential_disable = ACCESS_CREDENTIAL_DISABLE_NONE;
ac_descr[i].assigned_access_rights_count = 0;
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_Credential_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_CREDENTIALS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_Credential_Count(void)
{
return MAX_ACCESS_CREDENTIALS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_Credential_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_Credential_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_CREDENTIALS;
if (object_instance < MAX_ACCESS_CREDENTIALS) {
index = object_instance;
}
return index;
}
/* note: the object name must be unique within this device */
bool Access_Credential_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_CREDENTIALS) {
sprintf(text_string, "ACCESS CREDENTIAL %lu",
(unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_Credential_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_Credential_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_CREDENTIAL, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_Credential_Object_Name(
rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], OBJECT_ACCESS_CREDENTIAL);
break;
case PROP_GLOBAL_IDENTIFIER:
apdu_len = encode_application_unsigned(
&apdu[0], ac_descr[object_index].global_identifier);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], ac_descr[object_index].reliability);
break;
case PROP_CREDENTIAL_STATUS:
apdu_len = encode_application_enumerated(
&apdu[0], ac_descr[object_index].credential_status);
break;
case PROP_REASON_FOR_DISABLE:
for (i = 0; i < ac_descr[object_index].reasons_count; i++) {
len = encode_application_enumerated(
&apdu[0], ac_descr[object_index].reason_for_disable[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
break;
case PROP_AUTHENTICATION_FACTORS:
if (rpdata->array_index == 0) {
apdu_len = encode_application_unsigned(
&apdu[0], ac_descr[object_index].auth_factors_count);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0; i < ac_descr[object_index].auth_factors_count;
i++) {
len = bacapp_encode_credential_authentication_factor(
&apdu[0], &ac_descr[object_index].auth_factors[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <=
ac_descr[object_index].auth_factors_count) {
apdu_len =
bacapp_encode_credential_authentication_factor(&apdu[0],
&ac_descr[object_index]
.auth_factors[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_ACTIVATION_TIME:
apdu_len = bacapp_encode_datetime(
&apdu[0], &ac_descr[object_index].activation_time);
break;
case PROP_EXPIRATION_TIME:
apdu_len = bacapp_encode_datetime(
&apdu[0], &ac_descr[object_index].expiration_time);
break;
case PROP_CREDENTIAL_DISABLE:
apdu_len = encode_application_enumerated(
&apdu[0], ac_descr[object_index].credential_disable);
break;
case PROP_ASSIGNED_ACCESS_RIGHTS:
if (rpdata->array_index == 0) {
apdu_len = encode_application_unsigned(&apdu[0],
ac_descr[object_index].assigned_access_rights_count);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0;
i < ac_descr[object_index].assigned_access_rights_count;
i++) {
len = bacapp_encode_assigned_access_rights(&apdu[0],
&ac_descr[object_index].assigned_access_rights[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <=
ac_descr[object_index].assigned_access_rights_count) {
apdu_len = bacapp_encode_assigned_access_rights(&apdu[0],
&ac_descr[object_index]
.assigned_access_rights[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) &&
(rpdata->object_property != PROP_AUTHENTICATION_FACTORS) &&
(rpdata->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_Credential_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
unsigned object_index = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_AUTHENTICATION_FACTORS) &&
(wp_data->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index =
Access_Credential_Instance_To_Index(wp_data->object_instance);
switch (wp_data->object_property) {
case PROP_GLOBAL_IDENTIFIER:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
ac_descr[object_index].global_identifier =
value.type.Unsigned_Int;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
case PROP_CREDENTIAL_STATUS:
case PROP_REASON_FOR_DISABLE:
case PROP_AUTHENTICATION_FACTORS:
case PROP_ACTIVATION_TIME:
case PROP_EXPIRATION_TIME:
case PROP_CREDENTIAL_DISABLE:
case PROP_ASSIGNED_ACCESS_RIGHTS:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessCredential(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_Credential_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_CREDENTIAL;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_Credential_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_CREDENTIAL
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access Credential", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessCredential);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_CREDENTIAL */
#endif /* TEST */
+124
View File
@@ -0,0 +1,124 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_CREDENTIAL_H
#define ACCESS_CREDENTIAL_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/datetime.h"
#include "bacnet/timestamp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/assigned_access_rights.h"
#include "bacnet/credential_authentication_factor.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_CREDENTIALS
#define MAX_ACCESS_CREDENTIALS 4
#endif
#ifndef MAX_REASONS_FOR_DISABLE
#define MAX_REASONS_FOR_DISABLE 4
#endif
#ifndef MAX_AUTHENTICATION_FACTORS
#define MAX_AUTHENTICATION_FACTORS 4
#endif
#ifndef MAX_ASSIGNED_ACCESS_RIGHTS
#define MAX_ASSIGNED_ACCESS_RIGHTS 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t global_identifier;
BACNET_RELIABILITY reliability;
bool credential_status;
uint32_t reasons_count;
BACNET_ACCESS_CREDENTIAL_DISABLE_REASON
reason_for_disable[MAX_REASONS_FOR_DISABLE];
uint32_t auth_factors_count;
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR
auth_factors[MAX_AUTHENTICATION_FACTORS];
BACNET_DATE_TIME activation_time, expiration_time;
BACNET_ACCESS_CREDENTIAL_DISABLE credential_disable;
uint32_t assigned_access_rights_count;
BACNET_ASSIGNED_ACCESS_RIGHTS
assigned_access_rights[MAX_ASSIGNED_ACCESS_RIGHTS];
} ACCESS_CREDENTIAL_DESCR;
void Access_Credential_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_Credential_Valid_Instance(
uint32_t object_instance);
unsigned Access_Credential_Count(
void);
uint32_t Access_Credential_Index_To_Instance(
unsigned index);
unsigned Access_Credential_Instance_To_Index(
uint32_t instance);
bool Access_Credential_Object_Instance_Add(
uint32_t instance);
bool Access_Credential_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_Credential_Name_Set(
uint32_t object_instance,
char *new_name);
int Access_Credential_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_Credential_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_Credential_Create(
uint32_t object_instance);
bool Access_Credential_Delete(
uint32_t object_instance);
void Access_Credential_Cleanup(
void);
void Access_Credential_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessCredential(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
@@ -0,0 +1,45 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_CREDENTIAL
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_credential.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/assigned_access_rights.c \
$(SRC_DIR)/bacnet/authentication_factor.c \
$(SRC_DIR)/bacnet/credential_authentication_factor.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = access_credential
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+632
View File
@@ -0,0 +1,632 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access Door Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_door.h"
#include "bacnet/basic/services.h"
static bool Access_Door_Initialized = false;
static ACCESS_DOOR_DESCR ad_descr[MAX_ACCESS_DOORS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_RELIABILITY, PROP_OUT_OF_SERVICE,
PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, PROP_DOOR_PULSE_TIME,
PROP_DOOR_EXTENDED_PULSE_TIME, PROP_DOOR_OPEN_TOO_LONG_TIME, -1 };
static const int Properties_Optional[] = { PROP_DOOR_STATUS, PROP_LOCK_STATUS,
PROP_SECURED_STATUS, PROP_DOOR_UNLOCK_DELAY_TIME, PROP_DOOR_ALARM_STATE,
-1 };
static const int Properties_Proprietary[] = { -1 };
void Access_Door_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_Door_Init(void)
{
unsigned i, j;
if (!Access_Door_Initialized) {
Access_Door_Initialized = true;
/* initialize all the access door priority arrays to NULL */
for (i = 0; i < MAX_ACCESS_DOORS; i++) {
ad_descr[i].relinquish_default = DOOR_VALUE_LOCK;
ad_descr[i].event_state = EVENT_STATE_NORMAL;
ad_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
ad_descr[i].out_of_service = false;
ad_descr[i].door_status = DOOR_STATUS_CLOSED;
ad_descr[i].lock_status = LOCK_STATUS_LOCKED;
ad_descr[i].secured_status = DOOR_SECURED_STATUS_SECURED;
ad_descr[i].door_pulse_time = 30; /* 3s */
ad_descr[i].door_extended_pulse_time = 50; /* 5s */
ad_descr[i].door_unlock_delay_time = 0; /* 0s */
ad_descr[i].door_open_too_long_time = 300; /* 30s */
ad_descr[i].door_alarm_state = DOOR_ALARM_STATE_NORMAL;
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
ad_descr[i].value_active[j] = false;
/* just to fill in */
ad_descr[i].priority_array[j] = DOOR_VALUE_LOCK;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_Door_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_DOORS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_Door_Count(void)
{
return MAX_ACCESS_DOORS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_Door_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_Door_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_DOORS;
if (object_instance < MAX_ACCESS_DOORS) {
index = object_instance;
}
return index;
}
BACNET_DOOR_VALUE Access_Door_Present_Value(uint32_t object_instance)
{
unsigned index = 0;
unsigned i = 0;
BACNET_DOOR_VALUE value = DOOR_VALUE_LOCK;
index = Access_Door_Instance_To_Index(object_instance);
if (index < MAX_ACCESS_DOORS) {
value = ad_descr[i].relinquish_default;
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (ad_descr[index].value_active[i]) {
value = ad_descr[index].priority_array[i];
break;
}
}
}
return value;
}
unsigned Access_Door_Present_Value_Priority(uint32_t object_instance)
{
unsigned index = 0; /* instance to index conversion */
unsigned i = 0; /* loop counter */
unsigned priority = 0; /* return value */
index = Access_Door_Instance_To_Index(object_instance);
if (index < MAX_ACCESS_DOORS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (ad_descr[index].value_active[i]) {
priority = i + 1;
break;
}
}
}
return priority;
}
bool Access_Door_Present_Value_Set(
uint32_t object_instance, BACNET_DOOR_VALUE value, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Access_Door_Instance_To_Index(object_instance);
if (index < MAX_ACCESS_DOORS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) && (value >= DOOR_VALUE_LOCK) &&
(value <= DOOR_VALUE_EXTENDED_PULSE_UNLOCK)) {
ad_descr[index].value_active[priority - 1] = true;
ad_descr[index].priority_array[priority - 1] = value;
/* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no
priorities are set.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing output) */
status = true;
}
}
return status;
}
bool Access_Door_Present_Value_Relinquish(
uint32_t object_instance, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Access_Door_Instance_To_Index(object_instance);
if (index < MAX_ACCESS_DOORS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */)) {
ad_descr[index].value_active[priority - 1] = false;
/* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no
priorities are set.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing output) */
status = true;
}
}
return status;
}
BACNET_DOOR_VALUE Access_Door_Relinquish_Default(uint32_t object_instance)
{
BACNET_DOOR_VALUE status = -1;
unsigned index = 0;
index = Access_Door_Instance_To_Index(object_instance);
if (index < MAX_ACCESS_DOORS) {
return ad_descr[index].relinquish_default;
}
return status;
}
/* note: the object name must be unique within this device */
bool Access_Door_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_DOORS) {
sprintf(text_string, "ACCESS DOOR %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Access_Door_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Access_Door_Instance_To_Index(instance);
if (index < MAX_ACCESS_DOORS) {
oos_flag = ad_descr[index].out_of_service;
}
return oos_flag;
}
void Access_Door_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Access_Door_Instance_To_Index(instance);
if (index < MAX_ACCESS_DOORS) {
ad_descr[index].out_of_service = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_Door_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_Door_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_DOOR, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_Door_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_DOOR);
break;
case PROP_PRESENT_VALUE:
apdu_len = encode_application_enumerated(
&apdu[0], Access_Door_Present_Value(rpdata->object_instance));
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Access_Door_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].event_state);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].reliability);
break;
case PROP_OUT_OF_SERVICE:
state = Access_Door_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_PRIORITY_ARRAY:
/* Array element zero is the number of elements in the array */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (ad_descr[object_index].value_active[i]) {
len = encode_application_null(&apdu[apdu_len]);
} else {
len = encode_application_enumerated(&apdu[apdu_len],
ad_descr[object_index].priority_array[i]);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (ad_descr[object_index].value_active[i]) {
apdu_len = encode_application_null(&apdu[0]);
} else {
apdu_len =
encode_application_enumerated(&apdu[apdu_len],
ad_descr[object_index].priority_array[i]);
}
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_RELINQUISH_DEFAULT:
apdu_len = encode_application_enumerated(&apdu[0],
Access_Door_Relinquish_Default(rpdata->object_instance));
break;
case PROP_DOOR_STATUS:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].door_status);
break;
case PROP_LOCK_STATUS:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].lock_status);
break;
case PROP_SECURED_STATUS:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].secured_status);
break;
case PROP_DOOR_PULSE_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], ad_descr[object_index].door_pulse_time);
break;
case PROP_DOOR_EXTENDED_PULSE_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], ad_descr[object_index].door_extended_pulse_time);
break;
case PROP_DOOR_UNLOCK_DELAY_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], ad_descr[object_index].door_unlock_delay_time);
break;
case PROP_DOOR_OPEN_TOO_LONG_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], ad_descr[object_index].door_open_too_long_time);
break;
case PROP_DOOR_ALARM_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], ad_descr[object_index].door_alarm_state);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_Door_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
unsigned object_index = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index = Access_Door_Instance_To_Index(wp_data->object_instance);
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
status = Access_Door_Present_Value_Set(wp_data->object_instance,
value.type.Enumerated, wp_data->priority);
if (wp_data->priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
status = Access_Door_Present_Value_Relinquish(
wp_data->object_instance, wp_data->priority);
if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Access_Door_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_DOOR_STATUS:
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
ad_descr[object_index].door_status = value.type.Enumerated;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
break;
case PROP_LOCK_STATUS:
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
ad_descr[object_index].lock_status = value.type.Enumerated;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
break;
case PROP_DOOR_ALARM_STATE:
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
ad_descr[object_index].door_alarm_state =
value.type.Enumerated;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_RELIABILITY:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
case PROP_SECURED_STATUS:
case PROP_DOOR_PULSE_TIME:
case PROP_DOOR_EXTENDED_PULSE_TIME:
case PROP_DOOR_UNLOCK_DELAY_TIME:
case PROP_DOOR_OPEN_TOO_LONG_TIME:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessDoor(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_Door_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_DOOR;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_Door_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_DOOR
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access Door", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessDoor);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_DOOR */
#endif /* TEST */
+143
View File
@@ -0,0 +1,143 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_DOOR_H
#define ACCESS_DOOR_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_DOORS
#define MAX_ACCESS_DOORS 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
bool value_active[BACNET_MAX_PRIORITY];
BACNET_DOOR_VALUE priority_array[BACNET_MAX_PRIORITY];
BACNET_DOOR_VALUE relinquish_default;
BACNET_EVENT_STATE event_state;
BACNET_RELIABILITY reliability;
bool out_of_service;
BACNET_DOOR_STATUS door_status;
BACNET_LOCK_STATUS lock_status;
BACNET_DOOR_SECURED_STATUS secured_status;
uint32_t door_pulse_time, door_extended_pulse_time,
door_unlock_delay_time, door_open_too_long_time;
BACNET_DOOR_ALARM_STATE door_alarm_state;
} ACCESS_DOOR_DESCR;
void Access_Door_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_Door_Valid_Instance(
uint32_t object_instance);
unsigned Access_Door_Count(
void);
uint32_t Access_Door_Index_To_Instance(
unsigned index);
unsigned Access_Door_Instance_To_Index(
uint32_t instance);
bool Access_Door_Object_Instance_Add(
uint32_t instance);
BACNET_DOOR_VALUE Access_Door_Present_Value(
uint32_t object_instance);
unsigned Access_Door_Present_Value_Priority(
uint32_t object_instance);
bool Access_Door_Present_Value_Set(
uint32_t object_instance,
BACNET_DOOR_VALUE value,
unsigned priority);
bool Access_Door_Present_Value_Relinquish(
uint32_t object_instance,
unsigned priority);
BACNET_DOOR_VALUE Access_Door_Relinquish_Default(
uint32_t object_instance);
bool Access_Door_Relinquish_Default_Set(
uint32_t object_instance,
float value);
bool Access_Door_Change_Of_Value(
uint32_t instance);
void Access_Door_Change_Of_Value_Clear(
uint32_t instance);
bool Access_Door_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Access_Door_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_Door_Name_Set(
uint32_t object_instance,
char *new_name);
char *Access_Door_Description(
uint32_t instance);
bool Access_Door_Description_Set(
uint32_t instance,
char *new_name);
bool Access_Door_Out_Of_Service(
uint32_t instance);
void Access_Door_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
int Access_Door_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_Door_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_Door_Create(
uint32_t object_instance);
bool Access_Door_Delete(
uint32_t object_instance);
void Access_Door_Cleanup(
void);
void Access_Door_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessDoor(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_DOOR
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_door.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = access_door
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+431
View File
@@ -0,0 +1,431 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access Point Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_point.h"
#include "bacnet/basic/services.h"
static bool Access_Point_Initialized = false;
static ACCESS_POINT_DESCR ap_descr[MAX_ACCESS_POINTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_STATUS_FLAGS, PROP_EVENT_STATE,
PROP_RELIABILITY, PROP_OUT_OF_SERVICE, PROP_AUTHENTICATION_STATUS,
PROP_ACTIVE_AUTHENTICATION_POLICY, PROP_NUMBER_OF_AUTHENTICATION_POLICIES,
PROP_AUTHORIZATION_MODE, PROP_ACCESS_EVENT, PROP_ACCESS_EVENT_TAG,
PROP_ACCESS_EVENT_TIME, PROP_ACCESS_EVENT_CREDENTIAL, PROP_ACCESS_DOORS,
PROP_PRIORITY_FOR_WRITING, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Access_Point_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_Point_Init(void)
{
unsigned i;
if (!Access_Point_Initialized) {
Access_Point_Initialized = true;
for (i = 0; i < MAX_ACCESS_POINTS; i++) {
ap_descr[i].event_state = EVENT_STATE_NORMAL;
ap_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
ap_descr[i].out_of_service = false;
ap_descr[i].authentication_status = AUTHENTICATION_STATUS_NOT_READY;
ap_descr[i].active_authentication_policy = 0;
ap_descr[i].number_of_authentication_policies = 0;
ap_descr[i].authorization_mode = AUTHORIZATION_MODE_AUTHORIZE;
ap_descr[i].access_event = ACCESS_EVENT_NONE;
/* timestamp uninitialized */
/* access_event_credential should be set to some meaningful value */
ap_descr[i].num_doors = 0;
/* fill in the access doors with proper ids */
ap_descr[i].priority_for_writing = 16; /* lowest possible for now */
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_Point_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_POINTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_Point_Count(void)
{
return MAX_ACCESS_POINTS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_Point_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_Point_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_POINTS;
if (object_instance < MAX_ACCESS_POINTS) {
index = object_instance;
}
return index;
}
/* note: the object name must be unique within this device */
bool Access_Point_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_POINTS) {
sprintf(
text_string, "ACCESS POINT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Access_Point_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Access_Point_Instance_To_Index(instance);
if (index < MAX_ACCESS_POINTS) {
oos_flag = ap_descr[index].out_of_service;
}
return oos_flag;
}
void Access_Point_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Access_Point_Instance_To_Index(instance);
if (index < MAX_ACCESS_POINTS) {
ap_descr[index].out_of_service = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_Point_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_POINT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_Point_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_POINT);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Access_Point_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], ap_descr[object_index].event_state);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], ap_descr[object_index].reliability);
break;
case PROP_OUT_OF_SERVICE:
state = Access_Point_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_AUTHENTICATION_STATUS:
apdu_len = encode_application_enumerated(
&apdu[0], ap_descr[object_index].authentication_status);
break;
case PROP_ACTIVE_AUTHENTICATION_POLICY:
apdu_len = encode_application_unsigned(
&apdu[0], ap_descr[object_index].active_authentication_policy);
break;
case PROP_NUMBER_OF_AUTHENTICATION_POLICIES:
apdu_len = encode_application_unsigned(&apdu[0],
ap_descr[object_index].number_of_authentication_policies);
break;
case PROP_AUTHORIZATION_MODE:
apdu_len = encode_application_enumerated(
&apdu[0], ap_descr[object_index].authorization_mode);
break;
case PROP_ACCESS_EVENT:
apdu_len = encode_application_enumerated(
&apdu[0], ap_descr[object_index].access_event);
break;
case PROP_ACCESS_EVENT_TAG:
apdu_len = encode_application_unsigned(
&apdu[0], ap_descr[object_index].access_event_tag);
break;
case PROP_ACCESS_EVENT_TIME:
apdu_len = bacapp_encode_timestamp(
&apdu[0], &ap_descr[object_index].access_event_time);
break;
case PROP_ACCESS_EVENT_CREDENTIAL:
apdu_len = bacapp_encode_device_obj_ref(
&apdu[0], &ap_descr[object_index].access_event_credential);
break;
case PROP_ACCESS_DOORS:
if (rpdata->array_index == 0) {
apdu_len = encode_application_unsigned(
&apdu[0], ap_descr[object_index].num_doors);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0; i < ap_descr[object_index].num_doors; i++) {
len = bacapp_encode_device_obj_ref(
&apdu[0], &ap_descr[object_index].access_doors[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <= ap_descr[object_index].num_doors) {
apdu_len = bacapp_encode_device_obj_ref(&apdu[0],
&ap_descr[object_index]
.access_doors[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_ACCESS_DOORS) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_ACCESS_DOORS) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_RELIABILITY:
case PROP_OUT_OF_SERVICE:
case PROP_AUTHENTICATION_STATUS:
case PROP_ACTIVE_AUTHENTICATION_POLICY:
case PROP_NUMBER_OF_AUTHENTICATION_POLICIES:
case PROP_AUTHORIZATION_MODE:
case PROP_ACCESS_EVENT:
case PROP_ACCESS_EVENT_TAG:
case PROP_ACCESS_EVENT_TIME:
case PROP_ACCESS_EVENT_CREDENTIAL:
case PROP_ACCESS_DOORS:
case PROP_PRIORITY_FOR_WRITING:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessPoint(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_Point_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_POINT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_Point_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_POINT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access Point", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessPoint);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_POINT */
#endif /* TEST */
+120
View File
@@ -0,0 +1,120 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_POINT_H
#define ACCESS_POINT_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/timestamp.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_POINTS
#define MAX_ACCESS_POINTS 4
#endif
#ifndef MAX_ACCESS_DOORS_COUNT
#define MAX_ACCESS_DOORS_COUNT 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
BACNET_EVENT_STATE event_state;
BACNET_RELIABILITY reliability;
bool out_of_service;
BACNET_AUTHENTICATION_STATUS authentication_status;
uint32_t active_authentication_policy,
number_of_authentication_policies;
BACNET_AUTHORIZATION_MODE authorization_mode;
BACNET_ACCESS_EVENT access_event;
uint32_t access_event_tag;
BACNET_TIMESTAMP access_event_time;
BACNET_DEVICE_OBJECT_REFERENCE access_event_credential;
uint32_t num_doors; /* helper value, not a property */
BACNET_DEVICE_OBJECT_REFERENCE access_doors[MAX_ACCESS_DOORS_COUNT];
uint8_t priority_for_writing;
} ACCESS_POINT_DESCR;
void Access_Point_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_Point_Valid_Instance(
uint32_t object_instance);
unsigned Access_Point_Count(
void);
uint32_t Access_Point_Index_To_Instance(
unsigned index);
unsigned Access_Point_Instance_To_Index(
uint32_t instance);
bool Access_Point_Object_Instance_Add(
uint32_t instance);
bool Access_Point_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_Point_Name_Set(
uint32_t object_instance,
char *new_name);
bool Access_Point_Out_Of_Service(
uint32_t instance);
void Access_Point_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
int Access_Point_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_Point_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_Point_Create(
uint32_t object_instance);
bool Access_Point_Delete(
uint32_t object_instance);
void Access_Point_Cleanup(
void);
void Access_Point_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessPoint(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -0,0 +1,43 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_POINT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_point.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/timestamp.c \
$(TEST_DIR)/ctest.c
TARGET = access_point
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+404
View File
@@ -0,0 +1,404 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access Rights Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_rights.h"
#include "bacnet/basic/services.h"
static bool Access_Rights_Initialized = false;
static ACCESS_RIGHTS_DESCR ar_descr[MAX_ACCESS_RIGHTSS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_ENABLE,
PROP_NEGATIVE_ACCESS_RULES, PROP_POSITIVE_ACCESS_RULES, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Access_Rights_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_Rights_Init(void)
{
unsigned i;
if (!Access_Rights_Initialized) {
Access_Rights_Initialized = true;
for (i = 0; i < MAX_ACCESS_RIGHTSS; i++) {
ar_descr[i].global_identifier =
0; /* set to some meaningful value */
ar_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
ar_descr[i].enable = false;
ar_descr[i].negative_access_rules_count = 0;
ar_descr[i].positive_access_rules_count = 0;
/* fill in the positive and negative access rules with proper ids */
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_Rights_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_RIGHTSS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_Rights_Count(void)
{
return MAX_ACCESS_RIGHTSS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_Rights_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_Rights_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_RIGHTSS;
if (object_instance < MAX_ACCESS_RIGHTSS) {
index = object_instance;
}
return index;
}
/* note: the object name must be unique within this device */
bool Access_Rights_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_RIGHTSS) {
sprintf(
text_string, "ACCESS RIGHTS %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_Rights_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_Rights_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_RIGHTS, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_Rights_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_RIGHTS);
break;
case PROP_GLOBAL_IDENTIFIER:
apdu_len = encode_application_unsigned(
&apdu[0], ar_descr[object_index].global_identifier);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], ar_descr[object_index].reliability);
break;
case PROP_ENABLE:
apdu_len = encode_application_boolean(
&apdu[0], ar_descr[object_index].enable);
break;
case PROP_NEGATIVE_ACCESS_RULES:
if (rpdata->array_index == 0) {
apdu_len = encode_application_unsigned(&apdu[0],
ar_descr[object_index].negative_access_rules_count);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0;
i < ar_descr[object_index].negative_access_rules_count;
i++) {
len = bacapp_encode_access_rule(&apdu[0],
&ar_descr[object_index].negative_access_rules[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <=
ar_descr[object_index].negative_access_rules_count) {
apdu_len = bacapp_encode_access_rule(&apdu[0],
&ar_descr[object_index]
.negative_access_rules[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_POSITIVE_ACCESS_RULES:
if (rpdata->array_index == 0) {
apdu_len = encode_application_unsigned(&apdu[0],
ar_descr[object_index].positive_access_rules_count);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
for (i = 0;
i < ar_descr[object_index].positive_access_rules_count;
i++) {
len = bacapp_encode_access_rule(&apdu[0],
&ar_descr[object_index].positive_access_rules[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
if (rpdata->array_index <=
ar_descr[object_index].positive_access_rules_count) {
apdu_len = bacapp_encode_access_rule(&apdu[0],
&ar_descr[object_index]
.positive_access_rules[rpdata->array_index - 1]);
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) &&
(rpdata->object_property != PROP_NEGATIVE_ACCESS_RULES) &&
(rpdata->object_property != PROP_POSITIVE_ACCESS_RULES) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_Rights_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
unsigned object_index = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_NEGATIVE_ACCESS_RULES) &&
(wp_data->object_property != PROP_POSITIVE_ACCESS_RULES) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index = Access_Rights_Instance_To_Index(wp_data->object_instance);
switch (wp_data->object_property) {
case PROP_GLOBAL_IDENTIFIER:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
ar_descr[object_index].global_identifier =
value.type.Unsigned_Int;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
case PROP_ENABLE:
case PROP_NEGATIVE_ACCESS_RULES:
case PROP_POSITIVE_ACCESS_RULES:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessRights(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_Rights_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_RIGHTS;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_Rights_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_RIGHTS
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access Rights", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessRights);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_RIGHTS */
#endif /* TEST */
+110
View File
@@ -0,0 +1,110 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_RIGHTS_H
#define ACCESS_RIGHTS_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/access_rule.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_RIGHTSS
#define MAX_ACCESS_RIGHTSS 4
#endif
#ifndef MAX_NEGATIVE_ACCESS_RIGHTS_RULES
#define MAX_NEGATIVE_ACCESS_RIGHTS_RULES 4
#endif
#ifndef MAX_POSITIVE_ACCESS_RIGHTS_RULES
#define MAX_POSITIVE_ACCESS_RIGHTS_RULES 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t global_identifier;
BACNET_RELIABILITY reliability;
bool enable;
uint32_t negative_access_rules_count, positive_access_rules_count;
BACNET_ACCESS_RULE
negative_access_rules[MAX_NEGATIVE_ACCESS_RIGHTS_RULES];
BACNET_ACCESS_RULE
positive_access_rules[MAX_POSITIVE_ACCESS_RIGHTS_RULES];
} ACCESS_RIGHTS_DESCR;
void Access_Rights_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_Rights_Valid_Instance(
uint32_t object_instance);
unsigned Access_Rights_Count(
void);
uint32_t Access_Rights_Index_To_Instance(
unsigned index);
unsigned Access_Rights_Instance_To_Index(
uint32_t instance);
bool Access_Rights_Object_Instance_Add(
uint32_t instance);
bool Access_Rights_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_Rights_Name_Set(
uint32_t object_instance,
char *new_name);
int Access_Rights_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_Rights_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_Rights_Create(
uint32_t object_instance);
bool Access_Rights_Delete(
uint32_t object_instance);
void Access_Rights_Cleanup(
void);
void Access_Rights_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessRights(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -0,0 +1,43 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_RIGHTS
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_rights.c \
$(SRC_DIR)/bacnet/access_rule.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = access_rights
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+346
View File
@@ -0,0 +1,346 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access User Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_user.h"
#include "bacnet/basic/services.h"
static bool Access_User_Initialized = false;
static ACCESS_USER_DESCR au_descr[MAX_ACCESS_USERS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_USER_TYPE, PROP_CREDENTIALS, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Access_User_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_User_Init(void)
{
unsigned i;
if (!Access_User_Initialized) {
Access_User_Initialized = true;
for (i = 0; i < MAX_ACCESS_USERS; i++) {
au_descr[i].global_identifier =
0; /* set to some meaningful value */
au_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
au_descr[i].user_type = ACCESS_USER_TYPE_PERSON;
au_descr[i].credentials_count = 0;
/* fill in the credentials with proper ids */
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_User_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_USERS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_User_Count(void)
{
return MAX_ACCESS_USERS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_User_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_User_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_USERS;
if (object_instance < MAX_ACCESS_USERS) {
index = object_instance;
}
return index;
}
/* note: the object name must be unique within this device */
bool Access_User_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_USERS) {
sprintf(text_string, "ACCESS USER %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_User_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_User_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_USER, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_User_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_USER);
break;
case PROP_GLOBAL_IDENTIFIER:
apdu_len = encode_application_unsigned(
&apdu[0], au_descr[object_index].global_identifier);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], au_descr[object_index].reliability);
break;
case PROP_USER_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], au_descr[object_index].user_type);
break;
case PROP_CREDENTIALS:
for (i = 0; i < au_descr[object_index].credentials_count; i++) {
len = bacapp_encode_device_obj_ref(
&apdu[0], &au_descr[object_index].credentials[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_User_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
unsigned object_index = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index = Access_User_Instance_To_Index(wp_data->object_instance);
switch (wp_data->object_property) {
case PROP_GLOBAL_IDENTIFIER:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
au_descr[object_index].global_identifier =
value.type.Unsigned_Int;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
case PROP_USER_TYPE:
case PROP_CREDENTIALS:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessUser(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_User_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_USER;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_User_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_USER
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access User", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessUser);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_USER */
#endif /* TEST */
+103
View File
@@ -0,0 +1,103 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_USER_H
#define ACCESS_USER_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_USERS
#define MAX_ACCESS_USERS 4
#endif
#ifndef MAX_ACCESS_USER_CREDENTIALS_COUNT
#define MAX_ACCESS_USER_CREDENTIALS_COUNT 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t global_identifier;
BACNET_RELIABILITY reliability;
BACNET_ACCESS_USER_TYPE user_type;
uint32_t credentials_count;
BACNET_DEVICE_OBJECT_REFERENCE
credentials[MAX_ACCESS_USER_CREDENTIALS_COUNT];
} ACCESS_USER_DESCR;
void Access_User_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_User_Valid_Instance(
uint32_t object_instance);
unsigned Access_User_Count(
void);
uint32_t Access_User_Index_To_Instance(
unsigned index);
unsigned Access_User_Instance_To_Index(
uint32_t instance);
bool Access_User_Object_Instance_Add(
uint32_t instance);
bool Access_User_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_User_Name_Set(
uint32_t object_instance,
char *new_name);
int Access_User_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_User_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_User_Create(
uint32_t object_instance);
bool Access_User_Delete(
uint32_t object_instance);
void Access_User_Cleanup(
void);
void Access_User_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessUser(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_USER
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_user.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = access_user
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+411
View File
@@ -0,0 +1,411 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Access Zone Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "access_zone.h"
#include "bacnet/basic/services.h"
static bool Access_Zone_Initialized = false;
static ACCESS_ZONE_DESCR az_descr[MAX_ACCESS_ZONES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
PROP_OCCUPANCY_STATE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_RELIABILITY,
PROP_OUT_OF_SERVICE, PROP_ENTRY_POINTS, PROP_EXIT_POINTS, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Access_Zone_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Access_Zone_Init(void)
{
unsigned i;
if (!Access_Zone_Initialized) {
Access_Zone_Initialized = true;
for (i = 0; i < MAX_ACCESS_ZONES; i++) {
az_descr[i].global_identifier =
0; /* set to some meaningful value */
az_descr[i].occupancy_state = ACCESS_ZONE_OCCUPANCY_STATE_DISABLED;
az_descr[i].event_state = EVENT_STATE_NORMAL;
az_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
az_descr[i].out_of_service = false;
az_descr[i].entry_points_count = 0;
az_descr[i].exit_points_count = 0;
/* fill in the entry points and exit points with proper ids */
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Access_Zone_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ACCESS_ZONES) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Access_Zone_Count(void)
{
return MAX_ACCESS_ZONES;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Access_Zone_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Access_Zone_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ACCESS_ZONES;
if (object_instance < MAX_ACCESS_ZONES) {
index = object_instance;
}
return index;
}
/* note: the object name must be unique within this device */
bool Access_Zone_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ACCESS_ZONES) {
sprintf(text_string, "ACCESS ZONE %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Access_Zone_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Access_Zone_Instance_To_Index(instance);
if (index < MAX_ACCESS_ZONES) {
oos_flag = az_descr[index].out_of_service;
}
return oos_flag;
}
void Access_Zone_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Access_Zone_Instance_To_Index(instance);
if (index < MAX_ACCESS_ZONES) {
az_descr[index].out_of_service = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Access_Zone_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
object_index = Access_Zone_Instance_To_Index(rpdata->object_instance);
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ACCESS_ZONE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Access_Zone_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_ZONE);
break;
case PROP_GLOBAL_IDENTIFIER:
apdu_len = encode_application_unsigned(
&apdu[0], az_descr[object_index].global_identifier);
break;
case PROP_OCCUPANCY_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], az_descr[object_index].occupancy_state);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Access_Zone_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], az_descr[object_index].event_state);
break;
case PROP_RELIABILITY:
apdu_len = encode_application_enumerated(
&apdu[0], az_descr[object_index].reliability);
break;
case PROP_OUT_OF_SERVICE:
state = Access_Zone_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_ENTRY_POINTS:
for (i = 0; i < az_descr[object_index].entry_points_count; i++) {
len = bacapp_encode_device_obj_ref(
&apdu[0], &az_descr[object_index].entry_points[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
break;
case PROP_EXIT_POINTS:
for (i = 0; i < az_descr[object_index].exit_points_count; i++) {
len = bacapp_encode_device_obj_ref(
&apdu[0], &az_descr[object_index].exit_points[i]);
if (apdu_len + len < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Access_Zone_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
unsigned object_index = 0;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index = Access_Zone_Instance_To_Index(wp_data->object_instance);
switch (wp_data->object_property) {
case PROP_GLOBAL_IDENTIFIER:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
az_descr[object_index].global_identifier =
value.type.Unsigned_Int;
}
break;
case PROP_RELIABILITY:
if (Access_Zone_Out_Of_Service(wp_data->object_instance)) {
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
az_descr[object_index].reliability = value.type.Enumerated;
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_OCCUPANCY_STATE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_OUT_OF_SERVICE:
case PROP_ENTRY_POINTS:
case PROP_EXIT_POINTS:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testAccessZone(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Access_Zone_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ACCESS_ZONE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Access_Zone_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ACCESS_ZONE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Access Zone", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAccessZone);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ACCESS_ZONE */
#endif /* TEST */
+117
View File
@@ -0,0 +1,117 @@
/**************************************************************************
*
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef ACCESS_ZONE_H
#define ACCESS_ZONE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_ACCESS_ZONES
#define MAX_ACCESS_ZONES 4
#endif
#ifndef MAX_ACCESS_ZONE_ENTRY_POINTS
#define MAX_ACCESS_ZONE_ENTRY_POINTS 4
#endif
#ifndef MAX_ACCESS_ZONE_EXIT_POINTS
#define MAX_ACCESS_ZONE_EXIT_POINTS 4
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct {
uint32_t global_identifier;
BACNET_ACCESS_ZONE_OCCUPANCY_STATE occupancy_state;
BACNET_EVENT_STATE event_state;
BACNET_RELIABILITY reliability;
bool out_of_service;
uint32_t entry_points_count, exit_points_count;
BACNET_DEVICE_OBJECT_REFERENCE
entry_points[MAX_ACCESS_ZONE_ENTRY_POINTS];
BACNET_DEVICE_OBJECT_REFERENCE
exit_points[MAX_ACCESS_ZONE_EXIT_POINTS];
} ACCESS_ZONE_DESCR;
void Access_Zone_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Access_Zone_Valid_Instance(
uint32_t object_instance);
unsigned Access_Zone_Count(
void);
uint32_t Access_Zone_Index_To_Instance(
unsigned index);
unsigned Access_Zone_Instance_To_Index(
uint32_t instance);
bool Access_Zone_Object_Instance_Add(
uint32_t instance);
bool Access_Zone_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Access_Zone_Name_Set(
uint32_t object_instance,
char *new_name);
bool Access_Zone_Out_Of_Service(
uint32_t instance);
void Access_Zone_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
int Access_Zone_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Access_Zone_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Access_Zone_Create(
uint32_t object_instance);
bool Access_Zone_Delete(
uint32_t object_instance);
void Access_Zone_Cleanup(
void);
void Access_Zone_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAccessZone(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_ZONE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = access_zone.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = access_zone
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
File diff suppressed because it is too large Load Diff
+174
View File
@@ -0,0 +1,174 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef AI_H
#define AI_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h"
#include "bacnet/getevent.h"
#include "bacnet/alarm_ack.h"
#include "bacnet/get_alarm_sum.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct analog_input_descr {
unsigned Event_State:3;
float Present_Value;
BACNET_RELIABILITY Reliability;
bool Out_Of_Service;
uint8_t Units;
float Prior_Value;
float COV_Increment;
bool Changed;
#if defined(INTRINSIC_REPORTING)
uint32_t Time_Delay;
uint32_t Notification_Class;
float High_Limit;
float Low_Limit;
float Deadband;
unsigned Limit_Enable:2;
unsigned Event_Enable:3;
unsigned Notify_Type:1;
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
/* time to generate event notification */
uint32_t Remaining_Time_Delay;
/* AckNotification informations */
ACK_NOTIFICATION Ack_notify_data;
#endif
} ANALOG_INPUT_DESCR;
void Analog_Input_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Input_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Input_Count(
void);
uint32_t Analog_Input_Index_To_Instance(
unsigned index);
unsigned Analog_Input_Instance_To_Index(
uint32_t instance);
bool Analog_Input_Object_Instance_Add(
uint32_t instance);
bool Analog_Input_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Input_Name_Set(
uint32_t object_instance,
char *new_name);
char *Analog_Input_Description(
uint32_t instance);
bool Analog_Input_Description_Set(
uint32_t instance,
char *new_name);
bool Analog_Input_Units_Set(
uint32_t instance,
uint16_t units);
uint16_t Analog_Input_Units(
uint32_t instance);
int Analog_Input_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Input_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
float Analog_Input_Present_Value(
uint32_t object_instance);
void Analog_Input_Present_Value_Set(
uint32_t object_instance,
float value);
bool Analog_Input_Out_Of_Service(
uint32_t object_instance);
void Analog_Input_Out_Of_Service_Set(
uint32_t object_instance,
bool oos_flag);
bool Analog_Input_Change_Of_Value(
uint32_t instance);
void Analog_Input_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Input_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Input_COV_Increment(
uint32_t instance);
void Analog_Input_COV_Increment_Set(
uint32_t instance,
float value);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Analog_Input_Intrinsic_Reporting(
uint32_t object_instance);
#if defined(INTRINSIC_REPORTING)
int Analog_Input_Event_Information(
unsigned index,
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
int Analog_Input_Alarm_Ack(
BACNET_ALARM_ACK_DATA * alarmack_data,
BACNET_ERROR_CODE * error_code);
int Analog_Input_Alarm_Summary(
unsigned index,
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
#endif
bool Analog_Input_Create(
uint32_t object_instance);
bool Analog_Input_Delete(
uint32_t object_instance);
void Analog_Input_Cleanup(
void);
void Analog_Input_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalogInput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+43
View File
@@ -0,0 +1,43 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
HANDLER_DIR = ../handler
INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR)
DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_ANALOG_INPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = ai.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = analog_input
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+558
View File
@@ -0,0 +1,558 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Analog Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/basic/object/ao.h"
#include "bacnet/basic/services.h"
#ifndef MAX_ANALOG_OUTPUTS
#define MAX_ANALOG_OUTPUTS 4
#endif
/* we choose to have a NULL level in our system represented by */
/* a particular value. When the priorities are not in use, they */
/* will be relinquished (i.e. set to the NULL level). */
#define AO_LEVEL_NULL 255
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define AO_RELINQUISH_DEFAULT 0
/* Here is our Priority Array. They are supposed to be Real, but */
/* we don't have that kind of memory, so we will use a single byte */
/* and load a Real for returning the value when asked. */
static uint8_t Analog_Output_Level[MAX_ANALOG_OUTPUTS][BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_ANALOG_OUTPUTS];
/* we need to have our arrays initialized before answering any calls */
static bool Analog_Output_Initialized = false;
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_UNITS, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, -1 };
static const int Properties_Optional[] = { -1 };
static const int Properties_Proprietary[] = { -1 };
void Analog_Output_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
void Analog_Output_Init(void)
{
unsigned i, j;
if (!Analog_Output_Initialized) {
Analog_Output_Initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Analog_Output_Level[i][j] = AO_LEVEL_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Analog_Output_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ANALOG_OUTPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Analog_Output_Count(void)
{
return MAX_ANALOG_OUTPUTS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Analog_Output_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Analog_Output_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_ANALOG_OUTPUTS;
if (object_instance < MAX_ANALOG_OUTPUTS) {
index = object_instance;
}
return index;
}
float Analog_Output_Present_Value(uint32_t object_instance)
{
float value = AO_RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
value = Analog_Output_Level[index][i];
break;
}
}
}
return value;
}
unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance)
{
unsigned index = 0; /* instance to index conversion */
unsigned i = 0; /* loop counter */
unsigned priority = 0; /* return value */
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
priority = i + 1;
break;
}
}
}
return priority;
}
bool Analog_Output_Present_Value_Set(
uint32_t object_instance, float value, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) && (value >= 0.0) &&
(value <= 100.0)) {
Analog_Output_Level[index][priority - 1] = (uint8_t)value;
/* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no
priorities are set.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing output) */
status = true;
}
}
return status;
}
bool Analog_Output_Present_Value_Relinquish(
uint32_t object_instance, unsigned priority)
{
unsigned index = 0;
bool status = false;
index = Analog_Output_Instance_To_Index(object_instance);
if (index < MAX_ANALOG_OUTPUTS) {
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */)) {
Analog_Output_Level[index][priority - 1] = AO_LEVEL_NULL;
/* Note: you could set the physical output here to the next
highest priority, or to the relinquish default if no
priorities are set.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing output) */
status = true;
}
}
return status;
}
/* note: the object name must be unique within this device */
bool Analog_Output_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_ANALOG_OUTPUTS) {
sprintf(
text_string, "ANALOG OUTPUT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Analog_Output_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Analog_Output_Instance_To_Index(instance);
if (index < MAX_ANALOG_OUTPUTS) {
oos_flag = Out_Of_Service[index];
}
return oos_flag;
}
void Analog_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Analog_Output_Instance_To_Index(instance);
if (index < MAX_ANALOG_OUTPUTS) {
Out_Of_Service[index] = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
float real_value = (float)1.414;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_ANALOG_OUTPUT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Analog_Output_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_OUTPUT);
break;
case PROP_PRESENT_VALUE:
real_value = Analog_Output_Present_Value(rpdata->object_instance);
apdu_len = encode_application_real(&apdu[0], real_value);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Analog_Output_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Analog_Output_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_UNITS:
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT);
break;
case PROP_PRIORITY_ARRAY:
/* Array element zero is the number of elements in the array */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
object_index =
Analog_Output_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
real_value = Analog_Output_Level[object_index][i];
len = encode_application_real(
&apdu[apdu_len], real_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Analog_Output_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Analog_Output_Level[object_index][rpdata->array_index -
1] == AO_LEVEL_NULL) {
apdu_len = encode_application_null(&apdu[0]);
} else {
real_value =
Analog_Output_Level[object_index]
[rpdata->array_index - 1];
apdu_len =
encode_application_real(&apdu[0], real_value);
}
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_RELINQUISH_DEFAULT:
real_value = AO_RELINQUISH_DEFAULT;
apdu_len = encode_application_real(&apdu[0], real_value);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
status =
Analog_Output_Present_Value_Set(wp_data->object_instance,
value.type.Real, wp_data->priority);
if (wp_data->priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
status = Analog_Output_Present_Value_Relinquish(
wp_data->object_instance, wp_data->priority);
if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Analog_Output_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_UNITS:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
bool bResult;
/*
* start out assuming success and only set up error
* response if validation fails.
*/
bResult = true;
if (pValue->tag != ucExpectedTag) {
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
}
return (bResult);
}
void testAnalogOutput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Analog_Output_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_ANALOG_OUTPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Analog_Output_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_ANALOG_OUTPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Analog Output", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAnalogOutput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ANALOG_INPUT */
#endif /* TEST */
+139
View File
@@ -0,0 +1,139 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef AO_H
#define AO_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Analog_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Output_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Output_Count(
void);
uint32_t Analog_Output_Index_To_Instance(
unsigned index);
unsigned Analog_Output_Instance_To_Index(
uint32_t instance);
bool Analog_Output_Object_Instance_Add(
uint32_t instance);
float Analog_Output_Present_Value(
uint32_t object_instance);
unsigned Analog_Output_Present_Value_Priority(
uint32_t object_instance);
bool Analog_Output_Present_Value_Set(
uint32_t object_instance,
float value,
unsigned priority);
bool Analog_Output_Present_Value_Relinquish(
uint32_t object_instance,
unsigned priority);
float Analog_Output_Relinquish_Default(
uint32_t object_instance);
bool Analog_Output_Relinquish_Default_Set(
uint32_t object_instance,
float value);
bool Analog_Output_Change_Of_Value(
uint32_t instance);
void Analog_Output_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Output_COV_Increment(
uint32_t instance);
void Analog_Output_COV_Increment_Set(
uint32_t instance,
float value);
bool Analog_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Output_Name_Set(
uint32_t object_instance,
char *new_name);
char *Analog_Output_Description(
uint32_t instance);
bool Analog_Output_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Analog_Output_Reliability(
uint32_t object_instance);
bool Analog_Output_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
bool Analog_Output_Units_Set(
uint32_t instance,
uint16_t units);
uint16_t Analog_Output_Units(
uint32_t instance);
bool Analog_Output_Out_Of_Service(
uint32_t instance);
void Analog_Output_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
int Analog_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Analog_Output_Create(
uint32_t object_instance);
bool Analog_Output_Delete(
uint32_t object_instance);
void Analog_Output_Cleanup(
void);
void Analog_Output_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalogOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = ao.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = analog_output
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef AV_H
#define AV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h"
#include "bacnet/alarm_ack.h"
#include "bacnet/getevent.h"
#include "bacnet/get_alarm_sum.h"
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct analog_value_descr {
unsigned Event_State:3;
bool Out_Of_Service;
uint16_t Units;
float Present_Value;
float Prior_Value;
float COV_Increment;
bool Changed;
#if defined(INTRINSIC_REPORTING)
uint32_t Time_Delay;
uint32_t Notification_Class;
float High_Limit;
float Low_Limit;
float Deadband;
unsigned Limit_Enable:2;
unsigned Event_Enable:3;
unsigned Notify_Type:1;
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
/* time to generate event notification */
uint32_t Remaining_Time_Delay;
/* AckNotification informations */
ACK_NOTIFICATION Ack_notify_data;
#endif
} ANALOG_VALUE_DESCR;
void Analog_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Analog_Value_Valid_Instance(
uint32_t object_instance);
unsigned Analog_Value_Count(
void);
uint32_t Analog_Value_Index_To_Instance(
unsigned index);
unsigned Analog_Value_Instance_To_Index(
uint32_t object_instance);
bool Analog_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Analog_Value_Name_Set(
uint32_t object_instance,
char *new_name);
int Analog_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Analog_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Analog_Value_Present_Value_Set(
uint32_t object_instance,
float value,
uint8_t priority);
float Analog_Value_Present_Value(
uint32_t object_instance);
bool Analog_Value_Change_Of_Value(
uint32_t instance);
void Analog_Value_Change_Of_Value_Clear(
uint32_t instance);
bool Analog_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Analog_Value_COV_Increment(
uint32_t instance);
void Analog_Value_COV_Increment_Set(
uint32_t instance,
float value);
char *Analog_Value_Description(
uint32_t instance);
bool Analog_Value_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Analog_Value_Reliability(
uint32_t object_instance);
bool Analog_Value_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
uint16_t Analog_Value_Units(
uint32_t instance);
bool Analog_Value_Units_Set(
uint32_t instance,
uint16_t unit);
bool Analog_Value_Out_Of_Service(
uint32_t instance);
void Analog_Value_Out_Of_Service_Set(
uint32_t instance,
bool oos_flag);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Analog_Value_Intrinsic_Reporting(
uint32_t object_instance);
#if defined(INTRINSIC_REPORTING)
int Analog_Value_Event_Information(
unsigned index,
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
int Analog_Value_Alarm_Ack(
BACNET_ALARM_ACK_DATA * alarmack_data,
BACNET_ERROR_CODE * error_code);
int Analog_Value_Alarm_Summary(
unsigned index,
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
#endif
bool Analog_Value_Create(
uint32_t object_instance);
bool Analog_Value_Delete(
uint32_t object_instance);
void Analog_Value_Cleanup(
void);
void Analog_Value_Init(
void);
#ifdef TEST
#include "ctest.h"
void testAnalog_Value(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = av.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = analog_value
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+603
View File
@@ -0,0 +1,603 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bacnet/config.h"
#include "bacnet/basic/binding/address.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacapp.h"
#include "bacnet/datalink/datalink.h"
#include "bacnet/bacdcode.h"
#include "bacnet/npdu.h"
#include "bacnet/apdu.h"
#include "bacnet/basic/tsm/tsm.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/arf.h"
#include "bacnet/awf.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/object/bacfile.h"
typedef struct {
uint32_t instance;
char *filename;
} BACNET_FILE_LISTING;
#ifndef FILE_RECORD_SIZE
#define FILE_RECORD_SIZE MAX_OCTET_STRING_BYTES
#endif
static BACNET_FILE_LISTING BACnet_File_Listing[] = {
{ 0, "temp_0.txt" }, { 1, "temp_1.txt" }, { 2, "temp_2.txt" },
{ 0, NULL } /* last file indication */
};
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int bacfile_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_FILE_TYPE, PROP_FILE_SIZE,
PROP_MODIFICATION_DATE, PROP_ARCHIVE, PROP_READ_ONLY,
PROP_FILE_ACCESS_METHOD, -1 };
static const int bacfile_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int bacfile_Properties_Proprietary[] = { -1 };
void BACfile_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = bacfile_Properties_Required;
}
if (pOptional) {
*pOptional = bacfile_Properties_Optional;
}
if (pProprietary) {
*pProprietary = bacfile_Properties_Proprietary;
}
return;
}
static char *bacfile_name(uint32_t instance)
{
uint32_t index = 0;
char *filename = NULL;
/* linear search for file instance match */
while (BACnet_File_Listing[index].filename) {
if (BACnet_File_Listing[index].instance == instance) {
filename = BACnet_File_Listing[index].filename;
break;
}
index++;
}
return filename;
}
bool bacfile_object_name(
uint32_t instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
char *filename = NULL;
filename = bacfile_name(instance);
if (filename) {
status = characterstring_init_ansi(object_name, filename);
}
return status;
}
bool bacfile_valid_instance(uint32_t object_instance)
{
return bacfile_name(object_instance) ? true : false;
}
uint32_t bacfile_count(void)
{
uint32_t index = 0;
/* linear search for file instance match */
while (BACnet_File_Listing[index].filename) {
index++;
}
return index;
}
uint32_t bacfile_index_to_instance(unsigned find_index)
{
uint32_t instance = BACNET_MAX_INSTANCE + 1;
uint32_t index = 0;
/* bounds checking... */
while (BACnet_File_Listing[index].filename) {
if (index == find_index) {
instance = BACnet_File_Listing[index].instance;
break;
}
index++;
}
return instance;
}
static long fsize(FILE *pFile)
{
long size = 0;
long origin = 0;
if (pFile) {
origin = ftell(pFile);
fseek(pFile, 0L, SEEK_END);
size = ftell(pFile);
fseek(pFile, origin, SEEK_SET);
}
return (size);
}
unsigned bacfile_file_size(uint32_t object_instance)
{
char *pFilename = NULL;
FILE *pFile = NULL;
unsigned file_size = 0;
pFilename = bacfile_name(object_instance);
if (pFilename) {
pFile = fopen(pFilename, "rb");
if (pFile) {
file_size = fsize(pFile);
fclose(pFile);
}
}
return file_size;
}
/* return the number of bytes used, or -1 on error */
int bacfile_read_property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
char text_string[32] = { "" };
BACNET_CHARACTER_STRING char_string;
BACNET_DATE bdate;
BACNET_TIME btime;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_FILE, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
sprintf(text_string, "FILE %lu",
(unsigned long)rpdata->object_instance);
characterstring_init_ansi(&char_string, text_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_FILE);
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string, bacfile_name(rpdata->object_instance));
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_FILE_TYPE:
characterstring_init_ansi(&char_string, "TEXT");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_FILE_SIZE:
apdu_len = encode_application_unsigned(
&apdu[0], bacfile_file_size(rpdata->object_instance));
break;
case PROP_MODIFICATION_DATE:
/* FIXME: get the actual value instead of April Fool's Day */
bdate.year = 2006; /* AD */
bdate.month = 4; /* 1=Jan */
bdate.day = 1; /* 1..31 */
bdate.wday = 6; /* 1=Monday */
apdu_len = encode_application_date(&apdu[0], &bdate);
/* FIXME: get the actual value */
btime.hour = 7;
btime.min = 0;
btime.sec = 3;
btime.hundredths = 1;
apdu_len += encode_application_time(&apdu[apdu_len], &btime);
break;
case PROP_ARCHIVE:
/* 12.13.8 Archive
This property, of type BOOLEAN, indicates whether the File
object has been saved for historical or backup purposes. This
property shall be logical TRUE only if no changes have been
made to the file data by internal processes or through File
Access Services since the last time the object was archived.
*/
/* FIXME: get the actual value: note it may be inverse... */
apdu_len = encode_application_boolean(&apdu[0], true);
break;
case PROP_READ_ONLY:
/* FIXME: get the actual value */
apdu_len = encode_application_boolean(&apdu[0], true);
break;
case PROP_FILE_ACCESS_METHOD:
apdu_len = encode_application_enumerated(
&apdu[0], FILE_RECORD_AND_STREAM_ACCESS);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
return apdu_len;
}
/* returns true if successful */
bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
if (!bacfile_valid_instance(wp_data->object_instance)) {
wp_data->error_class = ERROR_CLASS_OBJECT;
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return false;
}
/* only array properties can have array options */
if (wp_data->array_index != BACNET_ARRAY_ALL) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* FIXME: len < application_data_len: more data? */
switch (wp_data->object_property) {
case PROP_ARCHIVE:
/* 12.13.8 Archive
This property, of type BOOLEAN, indicates whether the File
object has been saved for historical or backup purposes. This
property shall be logical TRUE only if no changes have been
made to the file data by internal processes or through File
Access Services since the last time the object was archived. */
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Boolean) {
/* FIXME: do something to wp_data->object_instance */
} else {
/* FIXME: do something to wp_data->object_instance */
}
}
break;
case PROP_FILE_SIZE:
/* If the file size can be changed by writing to the file,
and File_Access_Method is STREAM_ACCESS, then this property
shall be writable. */
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
/* FIXME: do something with value.type.Unsigned
to wp_data->object_instance */
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_DESCRIPTION:
case PROP_FILE_TYPE:
case PROP_MODIFICATION_DATE:
case PROP_READ_ONLY:
case PROP_FILE_ACCESS_METHOD:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
uint32_t bacfile_instance(char *filename)
{
uint32_t index = 0;
uint32_t instance = BACNET_MAX_INSTANCE + 1;
/* linear search for filename match */
while (BACnet_File_Listing[index].filename) {
if (strcmp(BACnet_File_Listing[index].filename, filename) == 0) {
instance = BACnet_File_Listing[index].instance;
break;
}
index++;
}
return instance;
}
#if MAX_TSM_TRANSACTIONS
/* this is one way to match up the invoke ID with */
/* the file ID from the AtomicReadFile request. */
/* Another way would be to store the */
/* invokeID and file instance in a list or table */
/* when the request was sent */
uint32_t bacfile_instance_from_tsm(uint8_t invokeID)
{
BACNET_NPDU_DATA npdu_data = { 0 }; /* dummy for getting npdu length */
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
uint8_t service_choice = 0;
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
BACNET_ADDRESS dest; /* where the original packet was destined */
uint8_t apdu[MAX_PDU] = { 0 }; /* original APDU packet */
uint16_t apdu_len = 0; /* original APDU packet length */
int len = 0; /* apdu header length */
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
uint32_t object_instance = BACNET_MAX_INSTANCE + 1; /* return value */
bool found = false;
found = tsm_get_transaction_pdu(
invokeID, &dest, &npdu_data, &apdu[0], &apdu_len);
if (found) {
if (!npdu_data.network_layer_message &&
npdu_data.data_expecting_reply &&
(apdu[0] == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len,
&service_data, &service_choice, &service_request,
&service_request_len);
if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
len = arf_decode_service_request(
service_request, service_request_len, &data);
if (len > 0) {
if (data.object_type == OBJECT_FILE) {
object_instance = data.object_instance;
}
}
}
}
}
return object_instance;
}
#endif
bool bacfile_read_stream_data(BACNET_ATOMIC_READ_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
size_t len = 0;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
len = fread(octetstring_value(&data->fileData[0]), 1,
data->type.stream.requestedOctetCount, pFile);
if (len < data->type.stream.requestedOctetCount) {
data->endOfFile = true;
} else {
data->endOfFile = false;
}
octetstring_truncate(&data->fileData[0], len);
fclose(pFile);
} else {
octetstring_truncate(&data->fileData[0], 0);
data->endOfFile = true;
}
} else {
octetstring_truncate(&data->fileData[0], 0);
data->endOfFile = true;
}
return found;
}
bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
if (data->type.stream.fileStartPosition == 0) {
/* open the file as a clean slate when starting at 0 */
pFile = fopen(pFilename, "wb");
} else if (data->type.stream.fileStartPosition == -1) {
/* If 'File Start Position' parameter has the special
value -1, then the write operation shall be treated
as an append to the current end of file. */
pFile = fopen(pFilename, "ab+");
} else {
/* open for update */
pFile = fopen(pFilename, "rb+");
}
if (pFile) {
if (data->type.stream.fileStartPosition != -1) {
(void)fseek(
pFile, data->type.stream.fileStartPosition, SEEK_SET);
}
if (fwrite(octetstring_value(&data->fileData[0]),
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
/* do something if it fails? */
}
fclose(pFile);
}
}
return found;
}
bool bacfile_write_record_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
{
char *pFilename = NULL;
bool found = false;
FILE *pFile = NULL;
uint32_t i = 0;
char dummy_data[FILE_RECORD_SIZE];
char *pData = NULL;
pFilename = bacfile_name(data->object_instance);
if (pFilename) {
found = true;
if (data->type.record.fileStartRecord == 0) {
/* open the file as a clean slate when starting at 0 */
pFile = fopen(pFilename, "wb");
} else if (data->type.record.fileStartRecord == -1) {
/* If 'File Start Record' parameter has the special
value -1, then the write operation shall be treated
as an append to the current end of file. */
pFile = fopen(pFilename, "ab+");
} else {
/* open for update */
pFile = fopen(pFilename, "rb+");
}
if (pFile) {
if ((data->type.record.fileStartRecord != -1) &&
(data->type.record.fileStartRecord > 0)) {
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
i++) {
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
if ((pData == NULL) || feof(pFile)) {
break;
}
}
}
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
if (fwrite(octetstring_value(&data->fileData[i]),
octetstring_length(&data->fileData[i]), 1,
pFile) != 1) {
/* do something if it fails? */
}
}
fclose(pFile);
}
}
return found;
}
bool bacfile_read_ack_stream_data(
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
{
bool found = false;
FILE *pFile = NULL;
char *pFilename = NULL;
pFilename = bacfile_name(instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
if (fwrite(octetstring_value(&data->fileData[0]),
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
(unsigned long)instance);
#endif
}
fclose(pFile);
}
}
return found;
}
bool bacfile_read_ack_record_data(
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
{
bool found = false;
FILE *pFile = NULL;
char *pFilename = NULL;
uint32_t i = 0;
char dummy_data[MAX_OCTET_STRING_BYTES] = { 0 };
char *pData = NULL;
pFilename = bacfile_name(instance);
if (pFilename) {
found = true;
pFile = fopen(pFilename, "rb");
if (pFile) {
if (data->type.record.fileStartRecord > 0) {
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
i++) {
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
if ((pData == NULL) || feof(pFile)) {
break;
}
}
}
for (i = 0; i < data->type.record.RecordCount; i++) {
if (fwrite(octetstring_value(&data->fileData[i]),
octetstring_length(&data->fileData[i]), 1,
pFile) != 1) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
(unsigned long)instance);
#endif
}
}
fclose(pFile);
}
}
return found;
}
void bacfile_init(void)
{
}
+108
View File
@@ -0,0 +1,108 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2005 Steve Karg
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
The Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA.
As a special exception, if other files instantiate templates or
use macros or inline functions from this file, or you compile
this file and link it with other works to produce a work based
on this file, this file does not by itself cause the resulting
work to be covered by the GNU General Public License. However
the source code for this file must still be made available in
accordance with section (3) of the GNU General Public License.
This exception does not invalidate any other reasons why a work
based on this file might be covered by the GNU General Public
License.
-------------------------------------------
####COPYRIGHTEND####*/
#ifndef BACFILE_H
#define BACFILE_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/apdu.h"
#include "bacnet/arf.h"
#include "bacnet/awf.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void BACfile_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool bacfile_object_name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool bacfile_valid_instance(
uint32_t object_instance);
uint32_t bacfile_count(
void);
uint32_t bacfile_index_to_instance(
unsigned find_index);
unsigned bacfile_instance_to_index(
uint32_t instance);
uint32_t bacfile_instance(
char *filename);
/* this is one way to match up the invoke ID with */
/* the file ID from the AtomicReadFile request. */
/* Another way would be to store the */
/* invokeID and file instance in a list or table */
/* when the request was sent */
uint32_t bacfile_instance_from_tsm(
uint8_t invokeID);
/* handler ACK helper */
bool bacfile_read_stream_data(
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_read_ack_stream_data(
uint32_t instance,
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_write_stream_data(
BACNET_ATOMIC_WRITE_FILE_DATA * data);
bool bacfile_read_record_data(
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_read_ack_record_data(
uint32_t instance,
BACNET_ATOMIC_READ_FILE_DATA * data);
bool bacfile_write_record_data(
BACNET_ATOMIC_WRITE_FILE_DATA * data);
void bacfile_init(
void);
uint32_t bacfile_file_size(
uint32_t instance);
/* handling for read property service */
int bacfile_read_property(
BACNET_READ_PROPERTY_DATA * rpdata);
/* handling for write property service */
bool bacfile_write_property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+552
View File
@@ -0,0 +1,552 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Binary Input Objects customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/cov.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/bi.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_INPUTS
#define MAX_BINARY_INPUTS 5
#endif
/* stores the current value */
static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS];
/* out of service decouples physical input from Present_Value */
static bool Out_Of_Service[MAX_BINARY_INPUTS];
/* Change of Value flag */
static bool Change_Of_Value[MAX_BINARY_INPUTS];
/* Polarity of Input */
static BACNET_POLARITY Polarity[MAX_BINARY_INPUTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Input_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, -1 };
static const int Binary_Input_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Binary_Input_Properties_Proprietary[] = { -1 };
void Binary_Input_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Input_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Input_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Input_Properties_Proprietary;
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Input_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_INPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Binary_Input_Count(void)
{
return MAX_BINARY_INPUTS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Binary_Input_Index_To_Instance(unsigned index)
{
return index;
}
void Binary_Input_Init(void)
{
static bool initialized = false;
unsigned i;
if (!initialized) {
initialized = true;
/* initialize all the values */
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
Present_Value[i] = BINARY_INACTIVE;
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Binary_Input_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_INPUTS;
if (object_instance < MAX_BINARY_INPUTS) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = BINARY_INACTIVE;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
value = Present_Value[index];
if (Polarity[index] != POLARITY_NORMAL) {
if (value == BINARY_INACTIVE) {
value = BINARY_ACTIVE;
} else {
value = BINARY_INACTIVE;
}
}
}
return value;
}
bool Binary_Input_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
value = Out_Of_Service[index];
}
return value;
}
bool Binary_Input_Change_Of_Value(uint32_t object_instance)
{
bool status = false;
unsigned index;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
status = Change_Of_Value[index];
}
return status;
}
void Binary_Input_Change_Of_Value_Clear(uint32_t object_instance)
{
unsigned index;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
Change_Of_Value[index] = false;
}
return;
}
/**
* For a given object instance-number, loads the value_list with the COV data.
*
* @param object_instance - object-instance number of the object
* @param value_list - list of COV data
*
* @return true if the value list is encoded
*/
bool Binary_Input_Encode_Value_List(
uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list)
{
bool status = false;
if (value_list) {
value_list->propertyIdentifier = PROP_PRESENT_VALUE;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
value_list->value.next = NULL;
value_list->value.type.Enumerated =
Binary_Input_Present_Value(object_instance);
value_list->priority = BACNET_NO_PRIORITY;
value_list = value_list->next;
}
if (value_list) {
value_list->propertyIdentifier = PROP_STATUS_FLAGS;
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
value_list->value.context_specific = false;
value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING;
value_list->value.next = NULL;
bitstring_init(&value_list->value.type.Bit_String);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false);
bitstring_set_bit(
&value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false);
if (Binary_Input_Out_Of_Service(object_instance)) {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, true);
} else {
bitstring_set_bit(&value_list->value.type.Bit_String,
STATUS_FLAG_OUT_OF_SERVICE, false);
}
value_list->priority = BACNET_NO_PRIORITY;
value_list->next = NULL;
status = true;
}
return status;
}
bool Binary_Input_Present_Value_Set(
uint32_t object_instance, BACNET_BINARY_PV value)
{
unsigned index = 0;
bool status = false;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
if (Polarity[index] != POLARITY_NORMAL) {
if (value == BINARY_INACTIVE) {
value = BINARY_ACTIVE;
} else {
value = BINARY_INACTIVE;
}
}
if (Present_Value[index] != value) {
Change_Of_Value[index] = true;
}
Present_Value[index] = value;
status = true;
}
return status;
}
void Binary_Input_Out_Of_Service_Set(uint32_t object_instance, bool value)
{
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
if (Out_Of_Service[index] != value) {
Change_Of_Value[index] = true;
}
Out_Of_Service[index] = value;
}
return;
}
bool Binary_Input_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
sprintf(
text_string, "BINARY INPUT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
BACNET_POLARITY Binary_Input_Polarity(uint32_t object_instance)
{
BACNET_POLARITY polarity = POLARITY_NORMAL;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
polarity = Polarity[index];
}
return polarity;
}
bool Binary_Input_Polarity_Set(
uint32_t object_instance, BACNET_POLARITY polarity)
{
bool status = false;
unsigned index = 0;
index = Binary_Input_Instance_To_Index(object_instance);
if (index < MAX_BINARY_INPUTS) {
Polarity[index] = polarity;
}
return status;
}
/* return apdu length, or BACNET_STATUS_ERROR on error */
/* assumption - object already exists, and has been bounds checked */
int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
bool state = false;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_INPUT, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
/* note: object name must be unique in our device */
Binary_Input_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT);
break;
case PROP_PRESENT_VALUE:
/* note: you need to look up the actual value */
apdu_len = encode_application_enumerated(
&apdu[0], Binary_Input_Present_Value(rpdata->object_instance));
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Binary_Input_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Binary_Input_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_POLARITY:
apdu_len = encode_application_enumerated(
&apdu[0], Binary_Input_Polarity(rpdata->object_instance));
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if (wp_data->array_index != BACNET_ARRAY_ALL) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Enumerated <= MAX_BINARY_PV) {
Binary_Input_Present_Value_Set(wp_data->object_instance,
(BACNET_BINARY_PV)value.type.Enumerated);
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Binary_Input_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_POLARITY:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Enumerated < MAX_POLARITY) {
Binary_Input_Polarity_Set(wp_data->object_instance,
(BACNET_POLARITY)value.type.Enumerated);
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testBinaryInput(Test *pTest)
{
BACNET_READ_PROPERTY_DATA rpdata;
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
Binary_Input_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_INPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_Input_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_BINARY_INPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary Input", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinaryInput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_INPUT */
#endif /* TEST */
+135
View File
@@ -0,0 +1,135 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef BI_H
#define BI_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/cov.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Input_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Input_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Input_Count(
void);
uint32_t Binary_Input_Index_To_Instance(
unsigned index);
unsigned Binary_Input_Instance_To_Index(
uint32_t instance);
bool Binary_Input_Object_Instance_Add(
uint32_t instance);
bool Binary_Input_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Input_Name_Set(
uint32_t object_instance,
char *new_name);
BACNET_BINARY_PV Binary_Input_Present_Value(
uint32_t object_instance);
bool Binary_Input_Present_Value_Set(
uint32_t object_instance,
BACNET_BINARY_PV value);
char *Binary_Input_Description(
uint32_t instance);
bool Binary_Input_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Binary_Input_Reliability(
uint32_t object_instance);
bool Binary_Input_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
char *Binary_Input_Inactive_Text(
uint32_t instance);
bool Binary_Input_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Input_Active_Text(
uint32_t instance);
bool Binary_Input_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_POLARITY Binary_Input_Polarity(
uint32_t object_instance);
bool Binary_Input_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Input_Out_Of_Service(
uint32_t object_instance);
void Binary_Input_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
bool Binary_Input_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Input_Change_Of_Value(
uint32_t instance);
void Binary_Input_Change_Of_Value_Clear(
uint32_t instance);
int Binary_Input_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Input_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Input_Create(
uint32_t object_instance);
bool Binary_Input_Delete(
uint32_t object_instance);
void Binary_Input_Cleanup(
void);
void Binary_Input_Init(
void);
#ifdef TEST
#include "ctest.h"
void testBinaryInput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BINARY_INPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bi.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = binary_input
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+519
View File
@@ -0,0 +1,519 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Binary Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/bo.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_OUTPUTS
#define MAX_BINARY_OUTPUTS 4
#endif
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define RELINQUISH_DEFAULT BINARY_INACTIVE
/* Here is our Priority Array.*/
static BACNET_BINARY_PV Binary_Output_Level[MAX_BINARY_OUTPUTS]
[BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_BINARY_OUTPUTS];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, PROP_PRIORITY_ARRAY,
PROP_RELINQUISH_DEFAULT, -1 };
static const int Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 };
static const int Binary_Output_Properties_Proprietary[] = { -1 };
void Binary_Output_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Output_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Output_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Output_Properties_Proprietary;
}
return;
}
void Binary_Output_Init(void)
{
unsigned i, j;
static bool initialized = false;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Binary_Output_Level[i][j] = BINARY_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Output_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_OUTPUTS) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Binary_Output_Count(void)
{
return MAX_BINARY_OUTPUTS;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Binary_Output_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Binary_Output_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_OUTPUTS;
if (object_instance < MAX_BINARY_OUTPUTS) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Binary_Output_Instance_To_Index(object_instance);
if (index < MAX_BINARY_OUTPUTS) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Binary_Output_Level[index][i] != BINARY_NULL) {
value = Binary_Output_Level[index][i];
break;
}
}
}
return value;
}
bool Binary_Output_Out_Of_Service(uint32_t object_instance)
{
bool value = false;
unsigned index = 0;
index = Binary_Output_Instance_To_Index(object_instance);
if (index < MAX_BINARY_OUTPUTS) {
value = Out_Of_Service[index];
}
return value;
}
/* note: the object name must be unique within this device */
bool Binary_Output_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_BINARY_OUTPUTS) {
sprintf(
text_string, "BINARY OUTPUT %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
BACNET_POLARITY polarity = POLARITY_NORMAL;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_OUTPUT, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Binary_Output_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT);
break;
case PROP_PRESENT_VALUE:
present_value =
Binary_Output_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
state = Out_Of_Service[object_index];
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_POLARITY:
apdu_len = encode_application_enumerated(&apdu[0], polarity);
break;
case PROP_PRIORITY_ARRAY:
/* Array element zero is the number of elements in the array */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Binary_Output_Level[object_index][i] == BINARY_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Output_Level[object_index][i];
len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Binary_Output_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Binary_Output_Level[object_index][rpdata->array_index -
1] == BINARY_NULL) {
apdu_len = encode_application_null(&apdu[apdu_len]);
} else {
present_value =
Binary_Output_Level[object_index]
[rpdata->array_index - 1];
apdu_len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_RELINQUISH_DEFAULT:
present_value = RELINQUISH_DEFAULT;
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_ACTIVE_TEXT:
characterstring_init_ansi(&char_string, "on");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_INACTIVE_TEXT:
characterstring_init_ansi(&char_string, "off");
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
unsigned int priority = 0;
BACNET_BINARY_PV level = BINARY_NULL;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
/* only array properties can have array options */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
priority = wp_data->priority;
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) &&
(value.type.Enumerated <= MAX_BINARY_PV)) {
level = (BACNET_BINARY_PV)value.type.Enumerated;
object_index = Binary_Output_Instance_To_Index(
wp_data->object_instance);
priority--;
Binary_Output_Level[object_index][priority] = level;
/* Note: you could set the physical output here if we
are the highest priority.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing
output) */
status = true;
} else if (priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = BINARY_NULL;
object_index = Binary_Output_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
Binary_Output_Level[object_index][priority] = level;
/* Note: you could set the physical output here to the
next highest priority, or to the relinquish default
if no priorities are set. However, if Out of Service
is TRUE, then don't set the physical output. This
comment may apply to the
main loop (i.e. check out of service before changing
output) */
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
object_index =
Binary_Output_Instance_To_Index(wp_data->object_instance);
Out_Of_Service[object_index] = value.type.Boolean;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_RELIABILITY:
case PROP_EVENT_STATE:
case PROP_POLARITY:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
case PROP_ACTIVE_TEXT:
case PROP_INACTIVE_TEXT:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testBinaryOutput(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Binary_Output_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_OUTPUT;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_Output_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_BINARY_OUTPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary Output", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinaryOutput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_INPUT */
#endif /* TEST */
+141
View File
@@ -0,0 +1,141 @@
/**************************************************************************
*
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef BO_H
#define BO_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Output_Init(
void);
void Binary_Output_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Output_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Output_Count(
void);
uint32_t Binary_Output_Index_To_Instance(
unsigned index);
unsigned Binary_Output_Instance_To_Index(
uint32_t instance);
bool Binary_Output_Object_Instance_Add(
uint32_t instance);
bool Binary_Output_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Output_Name_Set(
uint32_t object_instance,
char *new_name);
char *Binary_Output_Description(
uint32_t instance);
bool Binary_Output_Description_Set(
uint32_t instance,
char *new_name);
char *Binary_Output_Inactive_Text(
uint32_t instance);
bool Binary_Output_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Output_Active_Text(
uint32_t instance);
bool Binary_Output_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_BINARY_PV Binary_Output_Present_Value(
uint32_t instance);
bool Binary_Output_Present_Value_Set(
uint32_t instance,
BACNET_BINARY_PV binary_value,
unsigned priority);
bool Binary_Output_Present_Value_Relinquish(
uint32_t instance,
unsigned priority);
unsigned Binary_Output_Present_Value_Priority(
uint32_t object_instance);
BACNET_POLARITY Binary_Output_Polarity(
uint32_t instance);
bool Binary_Output_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Output_Out_Of_Service(
uint32_t instance);
void Binary_Output_Out_Of_Service_Set(
uint32_t object_instance,
bool value);
BACNET_BINARY_PV Binary_Output_Relinquish_Default(
uint32_t object_instance);
bool Binary_Output_Relinquish_Default_Set(
uint32_t object_instance,
BACNET_BINARY_PV value);
bool Binary_Output_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Output_Change_Of_Value(
uint32_t instance);
void Binary_Output_Change_Of_Value_Clear(
uint32_t instance);
int Binary_Output_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Output_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Output_Create(
uint32_t object_instance);
bool Binary_Output_Delete(
uint32_t object_instance);
void Binary_Output_Cleanup(
void);
#ifdef TEST
#include "ctest.h"
void testBinaryOutput(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_OUTPUT
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bo.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(SRC_DIR)/bacnet/indtext.c \
$(TEST_DIR)/ctest.c
TARGET = binary_output
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
+506
View File
@@ -0,0 +1,506 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/* Binary Output Objects - customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/wp.h"
#include "bacnet/rp.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/services.h"
#ifndef MAX_BINARY_VALUES
#define MAX_BINARY_VALUES 10
#endif
/* When all the priorities are level null, the present value returns */
/* the Relinquish Default value */
#define RELINQUISH_DEFAULT BINARY_INACTIVE
/* Here is our Priority Array.*/
static BACNET_BINARY_PV Binary_Value_Level[MAX_BINARY_VALUES]
[BACNET_MAX_PRIORITY];
/* Writable out-of-service allows others to play with our Present Value */
/* without changing the physical output */
static bool Out_Of_Service[MAX_BINARY_VALUES];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Binary_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, -1 };
static const int Binary_Value_Properties_Optional[] = { PROP_DESCRIPTION,
PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, -1 };
static const int Binary_Value_Properties_Proprietary[] = { -1 };
void Binary_Value_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Binary_Value_Properties_Required;
}
if (pOptional) {
*pOptional = Binary_Value_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Binary_Value_Properties_Proprietary;
}
return;
}
void Binary_Value_Init(void)
{
unsigned i, j;
static bool initialized = false;
if (!initialized) {
initialized = true;
/* initialize all the analog output priority arrays to NULL */
for (i = 0; i < MAX_BINARY_VALUES; i++) {
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
Binary_Value_Level[i][j] = BINARY_NULL;
}
}
}
return;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Binary_Value_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_BINARY_VALUES) {
return true;
}
return false;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then count how many you have */
unsigned Binary_Value_Count(void)
{
return MAX_BINARY_VALUES;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the instance */
/* that correlates to the correct index */
uint32_t Binary_Value_Index_To_Instance(unsigned index)
{
return index;
}
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need to return the index */
/* that correlates to the correct instance number */
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_BINARY_VALUES;
if (object_instance < MAX_BINARY_VALUES) {
index = object_instance;
}
return index;
}
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
{
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
unsigned index = 0;
unsigned i = 0;
index = Binary_Value_Instance_To_Index(object_instance);
if (index < MAX_BINARY_VALUES) {
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
if (Binary_Value_Level[index][i] != BINARY_NULL) {
value = Binary_Value_Level[index][i];
break;
}
}
}
return value;
}
/* note: the object name must be unique within this device */
bool Binary_Value_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
bool status = false;
if (object_instance < MAX_BINARY_VALUES) {
sprintf(
text_string, "BINARY VALUE %lu", (unsigned long)object_instance);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
bool Binary_Value_Out_Of_Service(uint32_t instance)
{
unsigned index = 0;
bool oos_flag = false;
index = Binary_Value_Instance_To_Index(instance);
if (index < MAX_BINARY_VALUES) {
oos_flag = Out_Of_Service[index];
}
return oos_flag;
}
void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
{
unsigned index = 0;
index = Binary_Value_Instance_To_Index(instance);
if (index < MAX_BINARY_VALUES) {
Out_Of_Service[index] = oos_flag;
}
}
/* return apdu len, or BACNET_STATUS_ERROR on error */
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int len = 0;
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
unsigned object_index = 0;
unsigned i = 0;
bool state = false;
uint8_t *apdu = NULL;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_BINARY_VALUE, rpdata->object_instance);
break;
/* note: Name and Description don't have to be the same.
You could make Description writable and different */
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Binary_Value_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
break;
case PROP_PRESENT_VALUE:
present_value = Binary_Value_Present_Value(rpdata->object_instance);
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
case PROP_STATUS_FLAGS:
/* note: see the details in the standard on how to use these */
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
state = Binary_Value_Out_Of_Service(rpdata->object_instance);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
/* note: see the details in the standard on how to use this */
apdu_len =
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
state = Binary_Value_Out_Of_Service(rpdata->object_instance);
apdu_len = encode_application_boolean(&apdu[0], state);
break;
case PROP_PRIORITY_ARRAY:
/* Array element zero is the number of elements in the array */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
/* if no index was specified, then try to encode the entire list
*/
/* into one packet. */
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
/* FIXME: check if we have room before adding it to APDU */
if (Binary_Value_Level[object_index][i] == BINARY_NULL) {
len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Value_Level[object_index][i];
len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
/* add it if we have room */
if ((apdu_len + len) < MAX_APDU) {
apdu_len += len;
} else {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
object_index =
Binary_Value_Instance_To_Index(rpdata->object_instance);
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
if (Binary_Value_Level[object_index][rpdata->array_index] ==
BINARY_NULL) {
apdu_len = encode_application_null(&apdu[apdu_len]);
} else {
present_value = Binary_Value_Level[object_index]
[rpdata->array_index];
apdu_len = encode_application_enumerated(
&apdu[apdu_len], present_value);
}
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
case PROP_RELINQUISH_DEFAULT:
present_value = RELINQUISH_DEFAULT;
apdu_len = encode_application_enumerated(&apdu[0], present_value);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/* returns true if successful */
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
unsigned int priority = 0;
BACNET_BINARY_PV level = BINARY_NULL;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
switch (wp_data->object_property) {
case PROP_PRESENT_VALUE:
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
priority = wp_data->priority;
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
(priority != 6 /* reserved */) &&
(value.type.Enumerated <= MAX_BINARY_PV)) {
level = (BACNET_BINARY_PV)value.type.Enumerated;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority--;
Binary_Value_Level[object_index][priority] = level;
/* Note: you could set the physical output here if we
are the highest priority.
However, if Out of Service is TRUE, then don't set the
physical output. This comment may apply to the
main loop (i.e. check out of service before changing
output) */
status = true;
} else if (priority == 6) {
/* Command priority 6 is reserved for use by Minimum On/Off
algorithm and may not be used for other purposes in any
object. */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
&wp_data->error_class, &wp_data->error_code);
if (status) {
level = BINARY_NULL;
object_index = Binary_Value_Instance_To_Index(
wp_data->object_instance);
priority = wp_data->priority;
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
priority--;
Binary_Value_Level[object_index][priority] = level;
/* Note: you could set the physical output here to the
next highest priority, or to the relinquish default
if no priorities are set. However, if Out of Service
is TRUE, then don't set the physical output. This
comment may apply to the
main loop (i.e. check out of service before changing
output) */
} else {
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
}
break;
case PROP_OUT_OF_SERVICE:
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
&wp_data->error_class, &wp_data->error_code);
if (status) {
Binary_Value_Out_Of_Service_Set(
wp_data->object_instance, value.type.Boolean);
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
case PROP_OBJECT_TYPE:
case PROP_STATUS_FLAGS:
case PROP_EVENT_STATE:
case PROP_PRIORITY_ARRAY:
case PROP_RELINQUISH_DEFAULT:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
pValue = pValue;
ucExpectedTag = ucExpectedTag;
pErrorClass = pErrorClass;
pErrorCode = pErrorCode;
return false;
}
void testBinary_Value(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint16_t decoded_type = 0;
uint32_t decoded_instance = 0;
BACNET_READ_PROPERTY_DATA rpdata;
Binary_Value_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_BINARY_VALUE;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Binary_Value_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
return;
}
#ifdef TEST_BINARY_VALUE
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Binary_Value", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBinary_Value);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BINARY_VALUE */
#endif /* TEST */
+152
View File
@@ -0,0 +1,152 @@
/**************************************************************************
*
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef BV_H
#define BV_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void Binary_Value_Init(
void);
void Binary_Value_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Binary_Value_Valid_Instance(
uint32_t object_instance);
unsigned Binary_Value_Count(
void);
uint32_t Binary_Value_Index_To_Instance(
unsigned index);
unsigned Binary_Value_Instance_To_Index(
uint32_t object_instance);
bool Binary_Value_Object_Instance_Add(
uint32_t instance);
bool Binary_Value_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Binary_Value_Name_Set(
uint32_t object_instance,
char *new_name);
char *Binary_Value_Description(
uint32_t instance);
bool Binary_Value_Description_Set(
uint32_t instance,
char *new_name);
BACNET_RELIABILITY Binary_Value_Reliability(
uint32_t object_instance);
bool Binary_Value_Reliability_Set(
uint32_t object_instance,
BACNET_RELIABILITY value);
char *Binary_Value_Inactive_Text(
uint32_t instance);
bool Binary_Value_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Value_Active_Text(
uint32_t instance);
bool Binary_Value_Active_Text_Set(
uint32_t instance,
char *new_name);
int Binary_Value_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Binary_Value_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
bool Binary_Value_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
bool Binary_Value_Change_Of_Value(
uint32_t instance);
void Binary_Value_Change_Of_Value_Clear(
uint32_t instance);
BACNET_BINARY_PV Binary_Value_Present_Value(
uint32_t instance);
bool Binary_Value_Present_Value_Set(
uint32_t instance,
BACNET_BINARY_PV value);
bool Binary_Value_Out_Of_Service(
uint32_t instance);
void Binary_Value_Out_Of_Service_Set(
uint32_t instance,
bool value);
char *Binary_Value_Description(
uint32_t instance);
bool Binary_Value_Description_Set(
uint32_t object_instance,
char *text_string);
char *Binary_Value_Inactive_Text(
uint32_t instance);
bool Binary_Value_Inactive_Text_Set(
uint32_t instance,
char *new_name);
char *Binary_Value_Active_Text(
uint32_t instance);
bool Binary_Value_Active_Text_Set(
uint32_t instance,
char *new_name);
BACNET_POLARITY Binary_Value_Polarity(
uint32_t instance);
bool Binary_Value_Polarity_Set(
uint32_t object_instance,
BACNET_POLARITY polarity);
bool Binary_Value_Create(
uint32_t object_instance);
bool Binary_Value_Delete(
uint32_t object_instance);
void Binary_Value_Cleanup(
void);
#ifdef TEST
#include "ctest.h"
void testBinary_Value(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+42
View File
@@ -0,0 +1,42 @@
#Makefile to build test case
CC = gcc
SRC_DIR = ../../src
TEST_DIR = ../../test
INCLUDES = -I../../include -I$(TEST_DIR) -I.
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_VALUE
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bv.c \
$(SRC_DIR)/bacnet/bacdcode.c \
$(SRC_DIR)/bacnet/bacint.c \
$(SRC_DIR)/bacnet/bacstr.c \
$(SRC_DIR)/bacnet/bacreal.c \
$(SRC_DIR)/bacnet/datetime.c \
$(SRC_DIR)/bacnet/bacapp.c \
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
$(SRC_DIR)/bacnet/bactext.c \
$(SRC_DIR)/bacnet/indtext.c \
$(SRC_DIR)/bacnet/lighting.c \
$(TEST_DIR)/ctest.c
TARGET = binary_value
all: ${TARGET}
OBJS = ${SRCS:.c=.o}
${TARGET}: ${OBJS}
${CC} -o $@ ${OBJS}
.c.o:
${CC} -c ${CFLAGS} $*.c -o $@
depend:
rm -f .depend
${CC} -MM ${CFLAGS} *.c >> .depend
clean:
rm -rf core ${TARGET} $(OBJS)
include: .depend
File diff suppressed because it is too large Load Diff
+203
View File
@@ -0,0 +1,203 @@
/**
* @file
* @author Steve Karg
* @date 2013
* @brief Channel objects, customize for your use
*
* @section DESCRIPTION
*
* The Channel object is a command object without a priority array, and the
* present-value property uses a priority array and a single precision floating point
* data type.
*
* @section LICENSE
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef CHANNEL_H
#define CHANNEL_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#include "bacnet/basic/object/lo.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests
Choose the datatypes that your application supports */
#if !(defined(CHANNEL_NUMERIC) || \
defined(CHANNEL_NULL) || \
defined(CHANNEL_BOOLEAN) || \
defined(CHANNEL_UNSIGNED) || \
defined(CHANNEL_SIGNED) || \
defined(CHANNEL_REAL) || \
defined(CHANNEL_DOUBLE) || \
defined(CHANNEL_OCTET_STRING) || \
defined(CHANNEL_CHARACTER_STRING) || \
defined(CHANNEL_BIT_STRING) || \
defined(CHANNEL_ENUMERATED) || \
defined(CHANNEL_DATE) || \
defined(CHANNEL_TIME) || \
defined(CHANNEL_OBJECT_ID) || \
defined(CHANNEL_LIGHTING_COMMAND))
#define CHANNEL_NUMERIC
#endif
#if defined (CHANNEL_NUMERIC)
#define CHANNEL_NULL
#define CHANNEL_BOOLEAN
#define CHANNEL_UNSIGNED
#define CHANNEL_SIGNED
#define CHANNEL_REAL
#define CHANNEL_DOUBLE
#define CHANNEL_ENUMERATED
#define CHANNEL_LIGHTING_COMMAND
#endif
typedef struct BACnet_Channel_Value_t {
uint8_t tag;
union {
/* NULL - not needed as it is encoded in the tag alone */
#if defined (CHANNEL_BOOLEAN)
bool Boolean;
#endif
#if defined (CHANNEL_UNSIGNED)
uint32_t Unsigned_Int;
#endif
#if defined (CHANNEL_SIGNED)
int32_t Signed_Int;
#endif
#if defined (CHANNEL_REAL)
float Real;
#endif
#if defined (CHANNEL_DOUBLE)
double Double;
#endif
#if defined (CHANNEL_OCTET_STRING)
BACNET_OCTET_STRING Octet_String;
#endif
#if defined (CHANNEL_CHARACTER_STRING)
BACNET_CHARACTER_STRING Character_String;
#endif
#if defined (CHANNEL_BIT_STRING)
BACNET_BIT_STRING Bit_String;
#endif
#if defined (CHANNEL_ENUMERATED)
uint32_t Enumerated;
#endif
#if defined (CHANNEL_DATE)
BACNET_DATE Date;
#endif
#if defined (CHANNEL_TIME)
BACNET_TIME Time;
#endif
#if defined (CHANNEL_OBJECT_ID)
BACNET_OBJECT_ID Object_Id;
#endif
#if defined (CHANNEL_LIGHTING_COMMAND)
BACNET_LIGHTING_COMMAND Lighting_Command;
#endif
} type;
/* simple linked list if needed */
struct BACnet_Channel_Value_t *next;
} BACNET_CHANNEL_VALUE;
void Channel_Property_Lists(const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Channel_Valid_Instance(uint32_t object_instance);
unsigned Channel_Count(void);
uint32_t Channel_Index_To_Instance(unsigned index);
unsigned Channel_Instance_To_Index(uint32_t instance);
bool Channel_Object_Instance_Add(uint32_t instance);
bool Channel_Object_Name(uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Channel_Name_Set(uint32_t object_instance,
char *new_name);
int Channel_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata);
bool Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_CHANNEL_VALUE * Channel_Present_Value(uint32_t object_instance);
bool Channel_Present_Value_Set(
BACNET_WRITE_PROPERTY_DATA * wp_data,
BACNET_APPLICATION_DATA_VALUE * value);
bool Channel_Out_Of_Service(uint32_t object_instance);
void Channel_Out_Of_Service_Set(uint32_t object_instance,
bool oos_flag);
unsigned Channel_Last_Priority(uint32_t object_instance);
BACNET_WRITE_STATUS Channel_Write_Status(uint32_t object_instance);
uint16_t Channel_Number(uint32_t object_instance);
bool Channel_Number_Set(uint32_t object_instance, uint16_t value);
unsigned Channel_Reference_List_Member_Count(uint32_t object_instance);
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *
Channel_Reference_List_Member_Element(uint32_t object_instance,
unsigned element);
bool Channel_Reference_List_Member_Element_Set(uint32_t object_instance,
unsigned array_index,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
unsigned Channel_Reference_List_Member_Element_Add(uint32_t object_instance,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
unsigned Channel_Reference_List_Member_Local_Add(
uint32_t object_instance,
uint16_t type,
uint32_t instance,
BACNET_PROPERTY_ID propertyIdentifier,
uint32_t arrayIndex);
uint16_t Channel_Control_Groups_Element(
uint32_t object_instance,
int32_t array_index);
bool Channel_Control_Groups_Element_Set(
uint32_t object_instance,
int32_t array_index,
uint16_t value);
bool Channel_Value_Copy(BACNET_CHANNEL_VALUE * cvalue,
BACNET_APPLICATION_DATA_VALUE * value);
int Channel_Value_Encode(uint8_t *apdu, int apdu_max,
BACNET_CHANNEL_VALUE * value);
int Channel_Coerce_Data_Encode(
uint8_t * apdu,
unsigned max_apdu,
BACNET_APPLICATION_DATA_VALUE * value,
BACNET_APPLICATION_TAG tag);
bool Channel_Write_Member_Value(
BACNET_WRITE_PROPERTY_DATA * wp_data,
BACNET_APPLICATION_DATA_VALUE * value);
void Channel_Init(void);
#ifdef TEST
#include "ctest.h"
void testChannelObject(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
File diff suppressed because it is too large Load Diff
+918
View File
@@ -0,0 +1,918 @@
/**
* @file
* @author Nikola Jelic
* @date 2014
* @brief Command objects, customize for your use
*
* @section DESCRIPTION
*
* The Command object type defines a standardized object whose
* properties represent the externally visible characteristics of a
* multi-action command procedure. A Command object is used to
* write a set of values to a group of object properties, based on
* the "action code" that is written to the Present_Value of the
* Command object. Whenever the Present_Value property of the
* Command object is written to, it triggers the Command object
* to take a set of actions that change the values of a set of other
* objects' properties.
*
* @section LICENSE
*
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bactext.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/services.h"
#include "bacnet/proplist.h"
#include "bacnet/timestamp.h"
#include "bacnet/basic/object/command.h"
/*BACnetActionCommand ::= SEQUENCE {
deviceIdentifier [0] BACnetObjectIdentifier OPTIONAL,
objectIdentifier [1] BACnetObjectIdentifier,
propertyIdentifier [2] BACnetPropertyIdentifier,
propertyArrayIndex [3] Unsigned OPTIONAL, --used only with array datatype
propertyValue [4] ABSTRACT-SYNTAX.&Type,
priority [5] Unsigned (1..16) OPTIONAL, --used only when property is commandable
postDelay [6] Unsigned OPTIONAL,
quitOnFailure [7] BOOLEAN,
writeSuccessful [8] BOOLEAN
}*/
int cl_encode_apdu(uint8_t *apdu, BACNET_ACTION_LIST *bcl)
{
int len = 0;
int apdu_len = 0;
if (bcl->Device_Id.instance >= 0 &&
bcl->Device_Id.instance <= BACNET_MAX_INSTANCE) {
len = encode_context_object_id(
&apdu[apdu_len], 0, bcl->Device_Id.type, bcl->Device_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
/* TODO: Check for object type and instance limits */
len = encode_context_object_id(
&apdu[apdu_len], 1, bcl->Object_Id.type, bcl->Object_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len =
encode_context_enumerated(&apdu[apdu_len], 2, bcl->Property_Identifier);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (bcl->Property_Array_Index != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(
&apdu[apdu_len], 3, bcl->Property_Array_Index);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
/* BACnet Testing Observed Incident oi00108
Command Action not correctly formatted
Revealed by BACnet Test Client v1.8.16 (
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00031 BC
135.1: 9.20.1.7 BC 135.1: 9.20.1.9 Any discussions can be directed to
edward@bac-test.com Please feel free to remove this comment when my
changes have been reviewed by all interested parties. Say 6 months ->
September 2016 */
len = encode_opening_tag(&apdu[apdu_len], 4);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len], &bcl->Value);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 4);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
if (bcl->Priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(&apdu[apdu_len], 5, bcl->Priority);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
if (bcl->Post_Delay != 0xFFFFFFFFU) {
len = encode_context_unsigned(&apdu[apdu_len], 6, bcl->Post_Delay);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
}
len = encode_context_boolean(&apdu[apdu_len], 7, bcl->Quit_On_Failure);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
len = encode_context_boolean(&apdu[apdu_len], 8, bcl->Write_Successful);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
apdu_len += len;
return apdu_len;
}
int cl_decode_apdu(uint8_t *apdu,
unsigned apdu_len,
BACNET_APPLICATION_TAG tag,
BACNET_ACTION_LIST *bcl)
{
int len = 0;
int dec_len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
if (decode_is_context_tag(&apdu[dec_len], 0)) {
/* Tag 0: Device ID */
dec_len++;
len = decode_object_id(
&apdu[dec_len], &bcl->Device_Id.type, &bcl->Device_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
}
if (!decode_is_context_tag(&apdu[dec_len++], 1)) {
return BACNET_STATUS_REJECT;
}
len = decode_object_id(
&apdu[dec_len], &bcl->Object_Id.type, &bcl->Object_Id.instance);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (tag_number != 2) {
return BACNET_STATUS_REJECT;
}
len = decode_enumerated(
&apdu[dec_len], len_value_type, &bcl->Property_Identifier);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (decode_is_context_tag(&apdu[dec_len], 3)) {
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(
&apdu[dec_len], len_value_type, &bcl->Property_Array_Index);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
} else {
bcl->Property_Array_Index = BACNET_ARRAY_ALL;
}
if (!decode_is_context_tag(&apdu[dec_len], 4)) {
return BACNET_STATUS_REJECT;
}
bcl->Value.context_specific = true;
bcl->Value.context_tag = 4;
bcl->Value.tag = tag;
switch (tag) {
case BACNET_APPLICATION_TAG_NULL:
len = 1;
break;
case BACNET_APPLICATION_TAG_BOOLEAN:
len = decode_context_boolean2(
&apdu[dec_len], 4, &bcl->Value.type.Boolean);
break;
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
len = decode_context_unsigned(
&apdu[dec_len], 4, &bcl->Value.type.Unsigned_Int);
break;
case BACNET_APPLICATION_TAG_SIGNED_INT:
len = decode_context_signed(
&apdu[dec_len], 4, &bcl->Value.type.Signed_Int);
break;
case BACNET_APPLICATION_TAG_REAL:
len = decode_context_real(&apdu[dec_len], 4, &bcl->Value.type.Real);
break;
case BACNET_APPLICATION_TAG_DOUBLE:
len = decode_context_double(
&apdu[dec_len], 4, &bcl->Value.type.Double);
break;
case BACNET_APPLICATION_TAG_OCTET_STRING:
len = decode_context_octet_string(
&apdu[dec_len], 4, &bcl->Value.type.Octet_String);
break;
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
len = decode_context_character_string(
&apdu[dec_len], 4, &bcl->Value.type.Character_String);
break;
case BACNET_APPLICATION_TAG_BIT_STRING:
len = decode_context_bitstring(
&apdu[dec_len], 4, &bcl->Value.type.Bit_String);
break;
case BACNET_APPLICATION_TAG_ENUMERATED:
len = decode_context_enumerated(
&apdu[dec_len], 4, &bcl->Value.type.Enumerated);
break;
case BACNET_APPLICATION_TAG_DATE:
len = decode_context_date(&apdu[dec_len], 4, &bcl->Value.type.Date);
break;
case BACNET_APPLICATION_TAG_TIME:
len = decode_context_bacnet_time(
&apdu[dec_len], 4, &bcl->Value.type.Time);
break;
case BACNET_APPLICATION_TAG_OBJECT_ID:
len = decode_context_object_id(&apdu[dec_len], 4,
&bcl->Value.type.Object_Id.type,
&bcl->Value.type.Object_Id.instance);
break;
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
len = lighting_command_decode(&apdu[dec_len], apdu_len - dec_len,
&bcl->Value.type.Lighting_Command);
break;
default:
return BACNET_STATUS_REJECT;
break;
}
if (len > 0) {
dec_len += len;
}
if (decode_is_context_tag(&apdu[dec_len], 5)) {
uint32_t priority_dec;
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(&apdu[dec_len], len_value_type, &priority_dec);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
bcl->Priority = (uint8_t)priority_dec;
dec_len += len;
} else {
bcl->Priority = BACNET_NO_PRIORITY;
}
if (decode_is_context_tag(&apdu[dec_len], 6)) {
len = decode_tag_number_and_value(
&apdu[dec_len], &tag_number, &len_value_type);
dec_len += len;
len = decode_unsigned(&apdu[dec_len], len_value_type, &bcl->Post_Delay);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
} else {
bcl->Post_Delay = 0xFFFFFFFFU;
}
if (!decode_is_context_tag(&apdu[dec_len], 7)) {
return BACNET_STATUS_REJECT;
}
len = decode_context_boolean2(&apdu[dec_len], 7, &bcl->Quit_On_Failure);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (!decode_is_context_tag(&apdu[dec_len], 8)) {
return BACNET_STATUS_REJECT;
}
len = decode_context_boolean2(&apdu[dec_len], 8, &bcl->Write_Successful);
if (len < 0) {
return BACNET_STATUS_REJECT;
}
dec_len += len;
if (dec_len < apdu_len) {
return BACNET_STATUS_REJECT;
}
return dec_len;
}
COMMAND_DESCR Command_Descr[MAX_COMMANDS];
/* These arrays are used by the ReadPropertyMultiple handler */
static const int Command_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_IN_PROCESS,
PROP_ALL_WRITES_SUCCESSFUL, PROP_ACTION, -1 };
static const int Command_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
static const int Command_Properties_Proprietary[] = { -1 };
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Command_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Command_Properties_Required;
}
if (pOptional) {
*pOptional = Command_Properties_Optional;
}
if (pProprietary) {
*pProprietary = Command_Properties_Proprietary;
}
return;
}
/**
* Initializes the Command object data
*/
void Command_Init(void)
{
unsigned i;
for (i = 0; i < MAX_COMMANDS; i++) {
Command_Descr[i].Present_Value = 0;
Command_Descr[i].In_Process = false;
Command_Descr[i].All_Writes_Successful = true; /* Optimistic default */
}
}
/**
* Determines if a given object instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Command_Valid_Instance(uint32_t object_instance)
{
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
return true;
}
return false;
}
/**
* Determines the number of this object instances
*
* @return Number of objects
*/
unsigned Command_Count(void)
{
return MAX_COMMANDS;
}
/**
* Determines the object instance-number for a given 0..N index
* of objects where N is the total number of this object instances.
*
* @param index - 0..total number of this object instances
*
* @return object instance-number for the given index
*/
uint32_t Command_Index_To_Instance(unsigned index)
{
return index;
}
/**
* For a given object instance-number, determines a 0..N index
* of this object where N is the total number of this object instances
*
* @param object_instance - object-instance number of the object.
*
* @return index for the given instance-number, or
* the total number of this object instances if not valid.
*/
unsigned Command_Instance_To_Index(uint32_t object_instance)
{
unsigned index = MAX_COMMANDS;
if (object_instance < MAX_COMMANDS) {
index = object_instance;
}
return index;
}
/**
* For a given object instance-number, determines the present-value
*
* @param object_instance - object-instance number of the object
*
* @return present-value of the object
*/
uint32_t Command_Present_Value(uint32_t object_instance)
{
uint32_t value = 0;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].Present_Value;
}
return value;
}
/**
* For a given object instance-number, sets the present-value
*
* @param object_instance - object-instance number of the object
* @param value - present-value to set
*
* @return true if values are within range and present-value is set.
*/
bool Command_Present_Value_Set(uint32_t object_instance, uint32_t value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].Present_Value = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, determines if the command
* is in-process. This true value indicates that the Command object has
* begun processing one of a set of action sequences. Once all of the
* writes have been attempted by the Command object, the In_Process
* property shall be set back to false.
*
* @param object_instance - object-instance number of the object
*
* @return true if this object-instance is in-process.
*/
bool Command_In_Process(uint32_t object_instance)
{
bool value = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].In_Process;
}
return value;
}
/**
* For a given object instance-number, sets the command in-process value.
*
* @param object_instance - object-instance number of the object
* @param value - true or false value to set
*
* @return true if values are within range and in-process flag is set.
*/
bool Command_In_Process_Set(uint32_t object_instance, bool value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].In_Process = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, indicates the success or failure
* of the sequence of actions that are triggered when the Present_Value
* property is written to.
*
* @param object_instance - object-instance number of the object
*
* @return true if all writes were successful for this object-instance
*/
bool Command_All_Writes_Successful(uint32_t object_instance)
{
bool value = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
value = Command_Descr[index].All_Writes_Successful;
}
return value;
}
/**
* For a given object instance-number, sets the all-writes-successful value.
*
* @param object_instance - object-instance number of the object
* @param value - true or false value to set
*
* @return true if values are within range and all-writes-succcessful is set.
*/
bool Command_All_Writes_Successful_Set(uint32_t object_instance, bool value)
{
bool status = false;
unsigned int index;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
Command_Descr[index].All_Writes_Successful = value;
status = true;
}
return status;
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Command_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
static char text_string[32] = ""; /* okay for single thread */
unsigned int index;
bool status = false;
index = Command_Instance_To_Index(object_instance);
if (index < MAX_COMMANDS) {
sprintf(text_string, "COMMAND %lu", (unsigned long)index);
status = characterstring_init_ansi(object_name, text_string);
}
return status;
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Command_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
int len = 0;
BACNET_CHARACTER_STRING char_string;
unsigned object_index = 0;
uint8_t *apdu = NULL;
uint16_t apdu_max = 0;
COMMAND_DESCR *CurrentCommand;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu_max = rpdata->application_data_len;
object_index = Command_Instance_To_Index(rpdata->object_instance);
if (object_index < MAX_COMMANDS) {
CurrentCommand = &Command_Descr[object_index];
} else {
return false;
}
apdu = rpdata->application_data;
switch ((int)rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], OBJECT_COMMAND, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
Command_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_COMMAND);
break;
case PROP_PRESENT_VALUE:
apdu_len = encode_application_unsigned(
&apdu[0], Command_Present_Value(rpdata->object_instance));
break;
case PROP_IN_PROCESS:
apdu_len = encode_application_boolean(
&apdu[0], Command_In_Process(rpdata->object_instance));
break;
case PROP_ALL_WRITES_SUCCESSFUL:
apdu_len = encode_application_boolean(&apdu[0],
Command_All_Writes_Successful(rpdata->object_instance));
break;
case PROP_ACTION:
/* TODO */
if (rpdata->array_index == 0) {
apdu_len =
encode_application_unsigned(&apdu[0], MAX_COMMAND_ACTIONS);
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
int i;
for (i = 0; i < MAX_COMMAND_ACTIONS; i++) {
BACNET_ACTION_LIST *Curr_CL_Member =
&CurrentCommand->Action[0];
/* another loop, for aditional actions in the list */
for (; Curr_CL_Member != NULL;
Curr_CL_Member = Curr_CL_Member->next) {
len = cl_encode_apdu(
&apdu[apdu_len], &CurrentCommand->Action[0]);
apdu_len += len;
/* assume the next one is of the same length, which need
* not be the case */
if ((i != MAX_COMMAND_ACTIONS - 1) &&
(apdu_len + len) >= apdu_max) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
}
} else {
if (rpdata->array_index < MAX_COMMAND_ACTIONS) {
BACNET_ACTION_LIST *Curr_CL_Member =
&CurrentCommand->Action[rpdata->array_index];
/* another loop, for aditional actions in the list */
for (; Curr_CL_Member != NULL;
Curr_CL_Member = Curr_CL_Member->next) {
len = cl_encode_apdu(
&apdu[apdu_len], &CurrentCommand->Action[0]);
apdu_len += len;
/* assume the next one is of the same length, which need
* not be the case */
if ((apdu_len + len) >= apdu_max) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
}
} else {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
apdu_len = BACNET_STATUS_ERROR;
}
}
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (rpdata->object_property != PROP_ACTION) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* WriteProperty handler for this object. For the given WriteProperty
* data, the application_data is loaded or the error flags are set.
*
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return false if an error is loaded, true if no errors
*/
bool Command_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* return value */
unsigned int object_index = 0;
int len = 0;
BACNET_APPLICATION_DATA_VALUE value;
/* decode the some of the request */
len = bacapp_decode_application_data(
wp_data->application_data, wp_data->application_data_len, &value);
/* FIXME: len < application_data_len: more data? */
if (len < 0) {
/* error while decoding - a value larger than we can handle */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
/* only array properties can have array options */
if ((wp_data->object_property != PROP_ACTION) &&
(wp_data->array_index != BACNET_ARRAY_ALL)) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return false;
}
object_index = Command_Instance_To_Index(wp_data->object_instance);
if (object_index >= MAX_COMMANDS) {
return false;
}
switch ((int)wp_data->object_property) {
case PROP_PRESENT_VALUE:
status =
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
&wp_data->error_class, &wp_data->error_code);
if (status) {
if (value.type.Unsigned_Int >= MAX_COMMAND_ACTIONS) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
return false;
}
Command_Present_Value_Set(
wp_data->object_instance, value.type.Unsigned_Int);
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
status = false;
}
break;
case PROP_OBJECT_IDENTIFIER:
case PROP_OBJECT_NAME:
case PROP_OBJECT_TYPE:
case PROP_IN_PROCESS:
case PROP_ALL_WRITES_SUCCESSFUL:
case PROP_ACTION:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
default:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
}
return status;
}
void Command_Intrinsic_Reporting(uint32_t object_instance)
{
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
uint8_t ucExpectedTag,
BACNET_ERROR_CLASS *pErrorClass,
BACNET_ERROR_CODE *pErrorCode)
{
bool bResult;
/*
* start out assuming success and only set up error
* response if validation fails.
*/
bResult = true;
if (pValue->tag != ucExpectedTag) {
bResult = false;
*pErrorClass = ERROR_CLASS_PROPERTY;
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
}
return (bResult);
}
void testCommand(Test *pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
uint32_t decoded_instance = 0;
uint16_t decoded_type = 0;
BACNET_READ_PROPERTY_DATA rpdata;
BACNET_ACTION_LIST clist, clist_test;
Command_Init();
rpdata.application_data = &apdu[0];
rpdata.application_data_len = sizeof(apdu);
rpdata.object_type = OBJECT_COMMAND;
rpdata.object_instance = 1;
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
rpdata.array_index = BACNET_ARRAY_ALL;
len = Command_Read_Property(&rpdata);
ct_test(pTest, len != 0);
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == rpdata.object_type);
ct_test(pTest, decoded_instance == rpdata.object_instance);
memset(&clist, 0, sizeof(BACNET_ACTION_LIST));
memset(&clist_test, 0, sizeof(BACNET_ACTION_LIST));
clist.Device_Id.type = OBJECT_DEVICE;
clist.Device_Id.instance = 3389;
clist.Object_Id.type = OBJECT_ANALOG_VALUE;
clist.Object_Id.instance = 42;
clist.Property_Identifier = PROP_PRESENT_VALUE;
clist.Property_Array_Index = BACNET_ARRAY_ALL;
clist.Value.tag = BACNET_APPLICATION_TAG_REAL;
clist.Value.type.Real = 39.0f;
clist.Priority = 4;
clist.Post_Delay = 0xFFFFFFFFU;
clist.Quit_On_Failure = true;
clist.Write_Successful = false;
clist.next = NULL;
len = cl_encode_apdu(apdu, &clist);
ct_test(pTest, len > 0);
len = cl_decode_apdu(apdu, len, BACNET_APPLICATION_TAG_REAL, &clist_test);
ct_test(pTest, len > 0);
ct_test(pTest, clist.Device_Id.type == clist_test.Device_Id.type);
ct_test(pTest, clist.Device_Id.instance == clist_test.Device_Id.instance);
ct_test(pTest, clist.Object_Id.type == clist_test.Object_Id.type);
ct_test(pTest, clist.Object_Id.instance == clist_test.Object_Id.instance);
ct_test(pTest, clist.Property_Identifier == clist_test.Property_Identifier);
ct_test(
pTest, clist.Property_Array_Index == clist_test.Property_Array_Index);
ct_test(pTest, clist.Value.tag == clist_test.Value.tag);
ct_test(pTest, clist.Value.type.Real == clist_test.Value.type.Real);
ct_test(pTest, clist.Priority == clist_test.Priority);
ct_test(pTest, clist.Post_Delay == clist_test.Post_Delay);
ct_test(pTest, clist.Quit_On_Failure == clist_test.Quit_On_Failure);
ct_test(pTest, clist.Write_Successful == clist_test.Write_Successful);
return;
}
#ifdef TEST_COMMAND
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Command", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCommand);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void)ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_COMMAND */
#endif /* TEST */
+175
View File
@@ -0,0 +1,175 @@
/**
* @file
* @author Nikola Jelic
* @date 2014
* @brief Command objects, customize for your use
*
* @section DESCRIPTION
*
* The Command object type defines a standardized object whose
* properties represent the externally visible characteristics of a
* multi-action command procedure. A Command object is used to
* write a set of values to a group of object properties, based on
* the "action code" that is written to the Present_Value of the
* Command object. Whenever the Present_Value property of the
* Command object is written to, it triggers the Command object
* to take a set of actions that change the values of a set of other
* objects' properties.
*
* @section LICENSE
*
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef COMMAND_H
#define COMMAND_H
#include <stdbool.h>
#include <stdint.h>
#include "bacnet/bacdef.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifndef MAX_COMMANDS
#define MAX_COMMANDS 4
#endif
#ifndef MAX_COMMAND_ACTIONS
#define MAX_COMMAND_ACTIONS 8
#endif
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
typedef struct bacnet_action_list {
BACNET_OBJECT_ID Device_Id; /* Optional */
BACNET_OBJECT_ID Object_Id;
BACNET_PROPERTY_ID Property_Identifier;
uint32_t Property_Array_Index; /* Conditional */
BACNET_APPLICATION_DATA_VALUE Value;
uint8_t Priority; /* Conditional */
uint32_t Post_Delay; /* Optional */
bool Quit_On_Failure;
bool Write_Successful;
struct bacnet_action_list *next;
} BACNET_ACTION_LIST;
int cl_encode_apdu(
uint8_t * apdu,
BACNET_ACTION_LIST * bcl);
int cl_decode_apdu(
uint8_t * apdu,
unsigned apdu_len,
BACNET_APPLICATION_TAG tag,
BACNET_ACTION_LIST * bcl);
typedef struct command_descr {
uint32_t Present_Value;
bool In_Process;
bool All_Writes_Successful;
BACNET_ACTION_LIST Action[MAX_COMMAND_ACTIONS];
} COMMAND_DESCR;
void Command_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Command_Valid_Instance(
uint32_t object_instance);
unsigned Command_Count(
void);
uint32_t Command_Index_To_Instance(
unsigned index);
unsigned Command_Instance_To_Index(
uint32_t instance);
bool Command_Object_Instance_Add(
uint32_t instance);
bool Command_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
bool Command_Name_Set(
uint32_t object_instance,
char *new_name);
char *Command_Description(
uint32_t instance);
bool Command_Description_Set(
uint32_t instance,
char *new_name);
int Command_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
bool Command_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
uint32_t Command_Present_Value(
uint32_t object_instance);
bool Command_Present_Value_Set(
uint32_t object_instance,
uint32_t value);
bool Command_In_Process(
uint32_t object_instance);
bool Command_In_Process_Set(
uint32_t object_instance,
bool value);
bool Command_All_Writes_Successful(
uint32_t object_instance);
bool Command_All_Writes_Successful_Set(
uint32_t object_instance,
bool value);
bool Command_Change_Of_Value(
uint32_t instance);
void Command_Change_Of_Value_Clear(
uint32_t instance);
bool Command_Encode_Value_List(
uint32_t object_instance,
BACNET_PROPERTY_VALUE * value_list);
float Command_COV_Increment(
uint32_t instance);
void Command_COV_Increment_Set(
uint32_t instance,
float value);
/* note: header of Intrinsic_Reporting function is required
even when INTRINSIC_REPORTING is not defined */
void Command_Intrinsic_Reporting(
uint32_t object_instance);
void Command_Init(
void);
#ifdef TEST
#include "ctest.h"
void testCommand(
Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

Some files were not shown because too many files have changed in this diff Show More