Issue 2 move folders and use deep path include file names to prevent collisions (#4)
* moving folders and files and adjust server demo build * Fix Makefile for apps/server on Linux * fix unit test source file folders * fix datetime convert UTC functions. Add Code::Blocks project for datetime testing * added some ignore extensions * disable parallel make option * fix build for abort, dcc, and epics apps * fix build for dcc, epics, error, and getevent apps. * Fixed building of all apps * fix the ipv4 to ipv6 router app build * Change indent style from Google to Webkit * make pretty to re-format style * removed common Makefile since we already had one and two was too many * remove scripts from root folder that are no longer maintained or used * remove mercurial EOL and ignore files for git repo * remove .vscodeconfig files from repo * tweak clang-format style * clang-format src and apps with tweaked style * added clang-tidy to fix readability if braces in src * result of make tidy for src and apps * fix clang-tidy mangling * Added code::blocks project for BACnet server simulation * added code::blocks linux project for WhoIs app * update text files for EOL * fix EOL in some files * fixed make win32 apps for older gcc * Removed Borland C++ Makefile in apps. Unable to maintain support for Borland C++ compiler. * created codeblocks project for apps/epics for Windows * fixing ports/xplained to work with new data structure. * fix ports/xplained example for Atmel Studio compile * fix ports/stm32f10x example for gcc Makefile compile * fix ports/stm32f10x example for IAR EWARM compile * fix ports/xplained timer callback * fix ports/bdk_atxx_mspt build with subdirs * fix ports/bdk_atxx_mspt build with subdirs * updated git ignore for IAR build artifacts * updated gitignore for non-tracked files and folders * fixed bdk-atxx4-mstp port for Rowley Crossworks project file * fixed bdk-atxx4-mstp port for GCC AVR Makefile * fixed atmega168 port for IAR AVR and GCC AVR Makefile * fixed at91sam7s port for IAR ARM and GCC ARM Makefile * removed unmaintainable DOS, RTOS32, and atmega8 ports. Updated rx62n (untested). * changed arm7 to uip port
This commit is contained in:
@@ -0,0 +1,237 @@
|
||||
/*####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####*/
|
||||
#include <stdint.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
|
||||
* structures when passing back abort status. Convert from error code to abort
|
||||
* code. Anything not defined converts to ABORT_REASON_OTHER. Will need
|
||||
* reworking if it is required to return proprietary abort codes.
|
||||
*/
|
||||
BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
BACNET_ABORT_REASON abort_code = ABORT_REASON_OTHER;
|
||||
|
||||
switch (error_code) {
|
||||
case ERROR_CODE_ABORT_BUFFER_OVERFLOW:
|
||||
abort_code = ABORT_REASON_BUFFER_OVERFLOW;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE:
|
||||
abort_code = ABORT_REASON_INVALID_APDU_IN_THIS_STATE;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK:
|
||||
abort_code = ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED:
|
||||
abort_code = ABORT_REASON_SEGMENTATION_NOT_SUPPORTED;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_PROPRIETARY:
|
||||
abort_code = ABORT_REASON_PROPRIETARY_FIRST;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_OTHER:
|
||||
default:
|
||||
abort_code = ABORT_REASON_OTHER;
|
||||
break;
|
||||
}
|
||||
|
||||
return (abort_code);
|
||||
}
|
||||
|
||||
/* encode service */
|
||||
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) {
|
||||
apdu[0] = PDU_TYPE_ABORT | 1;
|
||||
} else {
|
||||
apdu[0] = PDU_TYPE_ABORT;
|
||||
}
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = abort_reason;
|
||||
apdu_len = 3;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
#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 len = 0;
|
||||
|
||||
if (apdu_len > 0) {
|
||||
if (invoke_id) {
|
||||
*invoke_id = apdu[0];
|
||||
}
|
||||
if (abort_reason) {
|
||||
*abort_reason = apdu[1];
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#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 len = 0;
|
||||
|
||||
if (!apdu)
|
||||
return -1;
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu_len > 0) {
|
||||
if ((apdu[0] & 0xF0) != PDU_TYPE_ABORT)
|
||||
return -1;
|
||||
if (apdu[0] & 1)
|
||||
*server = true;
|
||||
else
|
||||
*server = false;
|
||||
if (apdu_len > 1) {
|
||||
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)
|
||||
{
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t test_invoke_id = 0;
|
||||
uint8_t test_abort_reason = 0;
|
||||
bool test_server = false;
|
||||
|
||||
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);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_abort_reason == abort_reason);
|
||||
ct_test(pTest, test_server == server);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void testAbort(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
uint8_t test_invoke_id = 0;
|
||||
uint8_t abort_reason = 0;
|
||||
uint8_t test_abort_reason = 0;
|
||||
bool server = false;
|
||||
bool test_server = false;
|
||||
|
||||
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);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_abort_reason == abort_reason);
|
||||
ct_test(pTest, test_server == server);
|
||||
|
||||
/* 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);
|
||||
ct_test(pTest, len == -1);
|
||||
|
||||
/* test NULL APDU */
|
||||
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);
|
||||
ct_test(pTest, len == 0);
|
||||
|
||||
/* check them all... */
|
||||
for (invoke_id = 0; invoke_id < 255; invoke_id++) {
|
||||
for (abort_reason = 0; abort_reason < 255; abort_reason++) {
|
||||
testAbortAPDU(pTest, invoke_id, abort_reason, false);
|
||||
testAbortAPDU(pTest, invoke_id, abort_reason, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST_ABORT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Abort", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAbort);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ABORT */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,182 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/access_rule.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
int bacapp_encode_access_rule(uint8_t *apdu, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_context_enumerated(&apdu[0], 0, rule->time_range_specifier);
|
||||
apdu_len += len;
|
||||
|
||||
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
||||
len = bacapp_encode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 1, &rule->time_range);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
len =
|
||||
encode_context_enumerated(&apdu[apdu_len], 2, rule->location_specifier);
|
||||
apdu_len += len;
|
||||
|
||||
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
||||
len = bacapp_encode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 3, &rule->location);
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
len = encode_context_boolean(&apdu[apdu_len], 4, rule->enable);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_access_rule(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_access_rule(&apdu[apdu_len], rule);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_access_rule(uint8_t *apdu, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(
|
||||
&apdu[apdu_len], 0, &rule->time_range_specifier);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rule->time_range_specifier == TIME_RANGE_SPECIFIER_SPECIFIED) {
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = bacapp_decode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 1, &rule->time_range);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_enumerated(
|
||||
&apdu[apdu_len], 2, &rule->location_specifier);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rule->location_specifier == LOCATION_SPECIFIER_SPECIFIED) {
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 3)) {
|
||||
len = bacapp_decode_context_device_obj_property_ref(
|
||||
&apdu[apdu_len], 3, &rule->location);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 4)) {
|
||||
len = decode_context_boolean2(&apdu[apdu_len], 4, &rule->enable);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_access_rule(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_ACCESS_RULE *rule)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
section_length = bacapp_decode_access_rule(&apdu[len], rule);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,262 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2009 John Minack
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/alarm_ack.h"
|
||||
|
||||
/** @file alarm_ack.c Handles Event Notifications (ACKs) */
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Creates an Unconfirmed Event Notification APDU
|
||||
**
|
||||
****************************************************/
|
||||
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 apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
||||
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
|
||||
apdu[2] = invoke_id;
|
||||
apdu[3] = SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM; /* service choice */
|
||||
apdu_len = 4;
|
||||
|
||||
len = alarm_ack_encode_service_request(&apdu[apdu_len], data);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Encodes the service data part of Event Notification
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_service_request(uint8_t *apdu, BACNET_ALARM_ACK_DATA *data)
|
||||
{
|
||||
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);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_context_object_id(&apdu[apdu_len], 1,
|
||||
(int)data->eventObjectIdentifier.type,
|
||||
data->eventObjectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_context_enumerated(
|
||||
&apdu[apdu_len], 2, data->eventStateAcked);
|
||||
apdu_len += len;
|
||||
|
||||
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);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_context_timestamp(
|
||||
&apdu[apdu_len], 5, &data->ackTimeStamp);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** 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 len = 0;
|
||||
int section_len;
|
||||
uint32_t enumValue;
|
||||
|
||||
/* unused parameter */
|
||||
apdu_len = apdu_len;
|
||||
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
if (-1 ==
|
||||
(section_len = decode_context_enumerated(&apdu[len], 2, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
data->eventStateAcked = (BACNET_EVENT_STATE)enumValue;
|
||||
len += section_len;
|
||||
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
if (-1 ==
|
||||
(section_len = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 5, &data->ackTimeStamp))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAlarmAck(Test *pTest)
|
||||
{
|
||||
BACNET_ALARM_ACK_DATA testAlarmAckIn;
|
||||
BACNET_ALARM_ACK_DATA testAlarmAckOut;
|
||||
|
||||
uint8_t buffer[MAX_APDU];
|
||||
int inLen;
|
||||
int outLen;
|
||||
|
||||
testAlarmAckIn.ackProcessIdentifier = 0x1234;
|
||||
characterstring_init_ansi(&testAlarmAckIn.ackSource, "This is a test");
|
||||
testAlarmAckIn.ackTimeStamp.tag = TIME_STAMP_SEQUENCE;
|
||||
testAlarmAckIn.ackTimeStamp.value.sequenceNum = 0x4331;
|
||||
testAlarmAckIn.eventObjectIdentifier.instance = 567;
|
||||
testAlarmAckIn.eventObjectIdentifier.type = OBJECT_DEVICE;
|
||||
testAlarmAckIn.eventTimeStamp.tag = TIME_STAMP_TIME;
|
||||
testAlarmAckIn.eventTimeStamp.value.time.hour = 10;
|
||||
testAlarmAckIn.eventTimeStamp.value.time.min = 11;
|
||||
testAlarmAckIn.eventTimeStamp.value.time.sec = 12;
|
||||
testAlarmAckIn.eventTimeStamp.value.time.hundredths = 14;
|
||||
testAlarmAckIn.eventStateAcked = EVENT_STATE_OFFNORMAL;
|
||||
|
||||
memset(&testAlarmAckOut, 0, sizeof(testAlarmAckOut));
|
||||
|
||||
inLen = alarm_ack_encode_service_request(buffer, &testAlarmAckIn);
|
||||
outLen = alarm_ack_decode_service_request(buffer, inLen, &testAlarmAckOut);
|
||||
|
||||
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.eventStateAcked == testAlarmAckOut.eventStateAcked);
|
||||
}
|
||||
|
||||
#ifdef TEST_ALARM_ACK
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Alarm Ack", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAlarmAck);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_ALARM_ACK */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,86 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2009 John Minack
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef ALARM_ACK_H_
|
||||
#define ALARM_ACK_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t ackProcessIdentifier;
|
||||
BACNET_OBJECT_ID eventObjectIdentifier;
|
||||
BACNET_EVENT_STATE eventStateAcked;
|
||||
BACNET_TIMESTAMP eventTimeStamp;
|
||||
BACNET_CHARACTER_STRING ackSource;
|
||||
BACNET_TIMESTAMP ackTimeStamp;
|
||||
} BACNET_ALARM_ACK_DATA;
|
||||
|
||||
/* return +1 if alarm was acknowledged
|
||||
return -1 if any error occurred
|
||||
return -2 abort */
|
||||
typedef int (
|
||||
*alarm_ack_function) (
|
||||
BACNET_ALARM_ACK_DATA * alarmack_data,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Creates a Alarm Acknowledge APDU
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Encodes the service data part of Alarm Acknowledge
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_encode_service_request(
|
||||
uint8_t * apdu,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
/***************************************************
|
||||
**
|
||||
** Decodes the service data part of Alarm Acknowledge
|
||||
**
|
||||
****************************************************/
|
||||
int alarm_ack_decode_service_request(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_ALARM_ACK_DATA * data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* ALARM_ACK_H_ */
|
||||
@@ -0,0 +1,51 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef APDU_H
|
||||
#define APDU_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct _confirmed_service_data {
|
||||
bool segmented_message;
|
||||
bool more_follows;
|
||||
bool segmented_response_accepted;
|
||||
int max_segs;
|
||||
int max_resp;
|
||||
uint8_t invoke_id;
|
||||
uint8_t sequence_number;
|
||||
uint8_t proposed_window_number;
|
||||
} BACNET_CONFIRMED_SERVICE_DATA;
|
||||
|
||||
typedef struct _confirmed_service_ack_data {
|
||||
bool segmented_message;
|
||||
bool more_follows;
|
||||
uint8_t invoke_id;
|
||||
uint8_t sequence_number;
|
||||
uint8_t proposed_window_number;
|
||||
} BACNET_CONFIRMED_SERVICE_ACK_DATA;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,520 @@
|
||||
/*####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####*/
|
||||
#include <stdint.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 apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
||||
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
|
||||
apdu[2] = invoke_id;
|
||||
apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
|
||||
apdu_len = 4;
|
||||
apdu_len += encode_application_object_id(
|
||||
&apdu[apdu_len], data->object_type, data->object_instance);
|
||||
switch (data->access) {
|
||||
case FILE_STREAM_ACCESS:
|
||||
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_unsigned(
|
||||
&apdu[apdu_len], data->type.stream.requestedOctetCount);
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 0);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += encode_application_signed(
|
||||
&apdu[apdu_len], data->type.record.fileStartRecord);
|
||||
apdu_len += encode_application_unsigned(
|
||||
&apdu[apdu_len], data->type.record.RecordCount);
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int arf_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
uint16_t type = 0; /* for decoding */
|
||||
|
||||
/* check for value pointers */
|
||||
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) {
|
||||
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)) {
|
||||
data->access = FILE_STREAM_ACCESS;
|
||||
/* 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);
|
||||
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);
|
||||
/* requestedOctetCount */
|
||||
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.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)) {
|
||||
data->access = FILE_RECORD_ACCESS;
|
||||
/* 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);
|
||||
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);
|
||||
/* RecordCount */
|
||||
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);
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} 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 len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
offset = 4;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len =
|
||||
arf_decode_service_request(&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* encode service */
|
||||
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;
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_COMPLEX_ACK;
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
|
||||
apdu_len = 3;
|
||||
/* endOfFile */
|
||||
apdu_len +=
|
||||
encode_application_boolean(&apdu[apdu_len], data->endOfFile);
|
||||
switch (data->access) {
|
||||
case FILE_STREAM_ACCESS:
|
||||
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_closing_tag(&apdu[apdu_len], 0);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += encode_application_signed(
|
||||
&apdu[apdu_len], data->type.record.fileStartRecord);
|
||||
apdu_len += encode_application_unsigned(
|
||||
&apdu[apdu_len], data->type.record.RecordCount);
|
||||
for (i = 0; i < data->type.record.RecordCount; i++) {
|
||||
apdu_len += encode_application_octet_string(
|
||||
&apdu[apdu_len], &data->fileData[i]);
|
||||
}
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
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;
|
||||
int decoded_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
/* check for value pointers */
|
||||
if (apdu_len && data) {
|
||||
len =
|
||||
decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type);
|
||||
if (tag_number != BACNET_APPLICATION_TAG_BOOLEAN) {
|
||||
return -1;
|
||||
}
|
||||
data->endOfFile = decode_boolean(len_value_type);
|
||||
if (decode_is_opening_tag_number(&apdu[len], 0)) {
|
||||
data->access = FILE_STREAM_ACCESS;
|
||||
/* 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);
|
||||
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);
|
||||
/* fileData */
|
||||
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]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
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)) {
|
||||
data->access = FILE_RECORD_ACCESS;
|
||||
/* 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);
|
||||
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);
|
||||
/* returnedRecordCount */
|
||||
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);
|
||||
for (i = 0; i < data->type.record.RecordCount; i++) {
|
||||
/* fileData */
|
||||
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]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
}
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
offset = 3;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len = arf_ack_decode_service_request(
|
||||
&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAtomicReadFileAckAccess(
|
||||
Test *pTest, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
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;
|
||||
uint8_t test_invoke_id = 0;
|
||||
unsigned int i = 0;
|
||||
|
||||
len = arf_ack_encode_apdu(&apdu[0], invoke_id, data);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
|
||||
len = arf_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
|
||||
ct_test(pTest, len != -1);
|
||||
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);
|
||||
} 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicReadFileAck(Test *pTest)
|
||||
{
|
||||
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));
|
||||
testAtomicReadFileAckAccess(pTest, &data);
|
||||
|
||||
data.endOfFile = false;
|
||||
data.access = FILE_RECORD_ACCESS;
|
||||
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));
|
||||
}
|
||||
testAtomicReadFileAckAccess(pTest, &data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void testAtomicReadFileAccess(Test *pTest, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
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;
|
||||
uint8_t test_invoke_id = 0;
|
||||
|
||||
len = arf_encode_apdu(&apdu[0], invoke_id, data);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
|
||||
len = arf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_data.object_type == data->object_type);
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicReadFile(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = 1;
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = 0;
|
||||
data.type.stream.requestedOctetCount = 128;
|
||||
testAtomicReadFileAccess(pTest, &data);
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = 2;
|
||||
data.access = FILE_RECORD_ACCESS;
|
||||
data.type.record.fileStartRecord = 1;
|
||||
data.type.record.RecordCount = 2;
|
||||
testAtomicReadFileAccess(pTest, &data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ATOMIC_READ_FILE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet AtomicReadFile", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAtomicReadFile);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testAtomicReadFileAck);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_xxx */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,127 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "bacnet/assigned_access_rights.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
int bacapp_encode_assigned_access_rights(
|
||||
uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = bacapp_encode_context_device_obj_ref(
|
||||
&apdu[apdu_len], 0, &aar->assigned_access_rights);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_boolean(&apdu[apdu_len], 1, aar->enable);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_assigned_access_rights(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_assigned_access_rights(&apdu[apdu_len], aar);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_assigned_access_rights(
|
||||
uint8_t *apdu, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = bacapp_decode_context_device_obj_ref(
|
||||
&apdu[apdu_len], 0, &aar->assigned_access_rights);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_boolean2(&apdu[apdu_len], 1, &aar->enable);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_assigned_access_rights(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_ASSIGNED_ACCESS_RIGHTS *aar)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
section_length = bacapp_decode_assigned_access_rights(&apdu[len], aar);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdint.h>
|
||||
#include "bacnet/authentication_factor.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
|
||||
int bacapp_encode_authentication_factor(
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 0, af->format_type);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 1, af->format_class);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_octet_string(&apdu[apdu_len], 2, &af->value);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_authentication_factor(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_authentication_factor(&apdu[apdu_len], af);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_authentication_factor(
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 0, &af->format_type);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 1, &af->format_class);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_octet_string(&apdu[apdu_len], 2, &af->value);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_authentication_factor(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
section_length = bacapp_decode_authentication_factor(&apdu[len], af);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <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)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_context_enumerated(&apdu[apdu_len], 0, aff->format_type);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
if (aff->format_type == AUTHENTICATION_FACTOR_CUSTOM) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 1, aff->vendor_id);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 2, aff->vendor_format);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
}
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_authentication_factor_format(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_authentication_factor_format(&apdu[apdu_len], aff);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_authentication_factor_format(
|
||||
uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 0)) {
|
||||
len = decode_context_enumerated(&apdu[apdu_len], 0, &aff->format_type);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 1)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 1, &aff->vendor_id);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM) &&
|
||||
(aff->vendor_id != 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
|
||||
len = decode_context_unsigned(&apdu[apdu_len], 2, &aff->vendor_format);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
}
|
||||
if ((aff->format_type != AUTHENTICATION_FACTOR_CUSTOM) &&
|
||||
(aff->vendor_format != 0)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_authentication_factor_format(
|
||||
uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR_FORMAT *aff)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
section_length =
|
||||
bacapp_decode_authentication_factor_format(&apdu[len], aff);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,437 @@
|
||||
/*####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####*/
|
||||
#include <stdint.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 apdu_len = 0; /* total length of the apdu, return value */
|
||||
uint32_t i = 0;
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
||||
apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU);
|
||||
apdu[2] = invoke_id;
|
||||
apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
|
||||
apdu_len = 4;
|
||||
apdu_len += encode_application_object_id(
|
||||
&apdu[apdu_len], data->object_type, data->object_instance);
|
||||
switch (data->access) {
|
||||
case FILE_STREAM_ACCESS:
|
||||
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_closing_tag(&apdu[apdu_len], 0);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
|
||||
apdu_len += encode_application_signed(
|
||||
&apdu[apdu_len], data->type.record.fileStartRecord);
|
||||
apdu_len += encode_application_unsigned(
|
||||
&apdu[apdu_len], data->type.record.returnedRecordCount);
|
||||
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
|
||||
apdu_len += encode_application_octet_string(
|
||||
&apdu[apdu_len], &data->fileData[i]);
|
||||
}
|
||||
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
int awf_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int tag_len = 0;
|
||||
int decoded_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
int32_t signed_value = 0;
|
||||
uint32_t unsigned_value = 0;
|
||||
uint16_t type = 0; /* for decoding */
|
||||
uint32_t i = 0;
|
||||
|
||||
/* check for value pointers */
|
||||
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) {
|
||||
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)) {
|
||||
data->access = FILE_STREAM_ACCESS;
|
||||
/* 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);
|
||||
len += tag_len;
|
||||
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);
|
||||
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]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
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)) {
|
||||
data->access = FILE_RECORD_ACCESS;
|
||||
/* 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);
|
||||
len += tag_len;
|
||||
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);
|
||||
len += tag_len;
|
||||
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);
|
||||
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]);
|
||||
if ((uint32_t)decoded_len != len_value_type) {
|
||||
return -1;
|
||||
}
|
||||
len += decoded_len;
|
||||
}
|
||||
if (!decode_is_closing_tag_number(&apdu[len], 1)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number is not extended so only one octet */
|
||||
len++;
|
||||
} 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 len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
offset = 4;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len =
|
||||
awf_decode_service_request(&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_COMPLEX_ACK;
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
|
||||
apdu_len = 3;
|
||||
switch (data->access) {
|
||||
case FILE_STREAM_ACCESS:
|
||||
apdu_len += encode_context_signed(
|
||||
&apdu[apdu_len], 0, data->type.stream.fileStartPosition);
|
||||
break;
|
||||
case FILE_RECORD_ACCESS:
|
||||
apdu_len += encode_context_signed(
|
||||
&apdu[apdu_len], 1, data->type.record.fileStartRecord);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* decode the service request only */
|
||||
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;
|
||||
uint32_t len_value_type = 0;
|
||||
|
||||
/* check for value pointers */
|
||||
if (apdu_len && data) {
|
||||
len =
|
||||
decode_tag_number_and_value(&apdu[0], &tag_number, &len_value_type);
|
||||
if (tag_number == 0) {
|
||||
data->access = FILE_STREAM_ACCESS;
|
||||
len += decode_signed(&apdu[len], len_value_type,
|
||||
&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 {
|
||||
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 len = 0;
|
||||
unsigned offset = 0;
|
||||
|
||||
if (!apdu) {
|
||||
return -1;
|
||||
}
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
offset = 3;
|
||||
|
||||
if (apdu_len > offset) {
|
||||
len = awf_ack_decode_service_request(
|
||||
&apdu[offset], apdu_len - offset, data);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testAtomicWriteFileAccess(Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
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;
|
||||
uint8_t test_invoke_id = 0;
|
||||
|
||||
len = awf_encode_apdu(&apdu[0], invoke_id, data);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
|
||||
len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_data.object_type == data->object_type);
|
||||
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);
|
||||
} 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,
|
||||
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 };
|
||||
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));
|
||||
testAtomicWriteFileAccess(pTest, &data);
|
||||
|
||||
data.object_type = OBJECT_FILE;
|
||||
data.object_instance = 1;
|
||||
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));
|
||||
testAtomicWriteFileAccess(pTest, &data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void testAtomicWriteFileAckAccess(
|
||||
Test *pTest, BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
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;
|
||||
uint8_t test_invoke_id = 0;
|
||||
|
||||
len = awf_encode_apdu(&apdu[0], invoke_id, data);
|
||||
ct_test(pTest, len != 0);
|
||||
apdu_len = len;
|
||||
|
||||
len = awf_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &test_data);
|
||||
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);
|
||||
} else if (test_data.access == FILE_RECORD_ACCESS) {
|
||||
ct_test(pTest,
|
||||
test_data.type.record.fileStartRecord ==
|
||||
data->type.record.fileStartRecord);
|
||||
}
|
||||
}
|
||||
|
||||
void testAtomicWriteFileAck(Test *pTest)
|
||||
{
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA data = { 0 };
|
||||
|
||||
data.access = FILE_STREAM_ACCESS;
|
||||
data.type.stream.fileStartPosition = 42;
|
||||
testAtomicWriteFileAckAccess(pTest, &data);
|
||||
|
||||
data.access = FILE_RECORD_ACCESS;
|
||||
data.type.record.fileStartRecord = 54;
|
||||
testAtomicWriteFileAckAccess(pTest, &data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ATOMIC_WRITE_FILE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet AtomicWriteFile", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAtomicWriteFile);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testAtomicWriteFileAck);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_WRITE_PROPERTY */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,101 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2007 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.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)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
if (dest && src) {
|
||||
dest->mac_len = src->mac_len;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
dest->mac[i] = src->mac[i];
|
||||
}
|
||||
dest->net = src->net;
|
||||
dest->len = src->len;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
dest->adr[i] = src->adr[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bacnet_address_same(BACNET_ADDRESS *dest, BACNET_ADDRESS *src)
|
||||
{
|
||||
uint8_t i = 0; /* loop counter */
|
||||
uint8_t max_len = 0; /* used for dynamic max */
|
||||
|
||||
if (dest == src) { /* same ? */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dest->net != src->net) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest->len != src->len) {
|
||||
return false;
|
||||
}
|
||||
|
||||
max_len = dest->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]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (dest->net == 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACADDR_H
|
||||
#define BACADDR_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bacnet_address_copy(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
bool bacnet_address_same(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
+2130
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,256 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACAPP_H
|
||||
#define BACAPP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#if defined (BACAPP_LIGHTING_COMMAND)
|
||||
#include "bacnet/lighting.h"
|
||||
#endif
|
||||
#if defined (BACAPP_DEVICE_OBJECT_PROP_REF)
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#endif
|
||||
|
||||
|
||||
struct BACnet_Application_Data_Value;
|
||||
typedef struct BACnet_Application_Data_Value {
|
||||
bool context_specific; /* true if context specific data */
|
||||
uint8_t context_tag; /* only used for context specific data */
|
||||
uint8_t tag; /* application tag data type */
|
||||
union {
|
||||
/* NULL - not needed as it is encoded in the tag alone */
|
||||
#if defined (BACAPP_BOOLEAN)
|
||||
bool Boolean;
|
||||
#endif
|
||||
#if defined (BACAPP_UNSIGNED)
|
||||
uint32_t Unsigned_Int;
|
||||
#endif
|
||||
#if defined (BACAPP_SIGNED)
|
||||
int32_t Signed_Int;
|
||||
#endif
|
||||
#if defined (BACAPP_REAL)
|
||||
float Real;
|
||||
#endif
|
||||
#if defined (BACAPP_DOUBLE)
|
||||
double Double;
|
||||
#endif
|
||||
#if defined (BACAPP_OCTET_STRING)
|
||||
BACNET_OCTET_STRING Octet_String;
|
||||
#endif
|
||||
#if defined (BACAPP_CHARACTER_STRING)
|
||||
BACNET_CHARACTER_STRING Character_String;
|
||||
#endif
|
||||
#if defined (BACAPP_BIT_STRING)
|
||||
BACNET_BIT_STRING Bit_String;
|
||||
#endif
|
||||
#if defined (BACAPP_ENUMERATED)
|
||||
uint32_t Enumerated;
|
||||
#endif
|
||||
#if defined (BACAPP_DATE)
|
||||
BACNET_DATE Date;
|
||||
#endif
|
||||
#if defined (BACAPP_TIME)
|
||||
BACNET_TIME Time;
|
||||
#endif
|
||||
#if defined (BACAPP_OBJECT_ID)
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
#endif
|
||||
#if defined (BACAPP_LIGHTING_COMMAND)
|
||||
BACNET_LIGHTING_COMMAND Lighting_Command;
|
||||
#endif
|
||||
#if defined (BACAPP_DEVICE_OBJECT_PROP_REF)
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE
|
||||
Device_Object_Property_Reference;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
struct BACnet_Application_Data_Value *next;
|
||||
} BACNET_APPLICATION_DATA_VALUE;
|
||||
|
||||
struct BACnet_Access_Error;
|
||||
typedef struct BACnet_Access_Error {
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
} BACNET_ACCESS_ERROR;
|
||||
|
||||
struct BACnet_Property_Reference;
|
||||
typedef struct BACnet_Property_Reference {
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t propertyArrayIndex; /* optional */
|
||||
/* either value or error, but not both.
|
||||
Use NULL value to indicate error */
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
BACNET_ACCESS_ERROR error;
|
||||
/* simple linked list */
|
||||
struct BACnet_Property_Reference *next;
|
||||
} BACNET_PROPERTY_REFERENCE;
|
||||
|
||||
struct BACnet_Property_Value;
|
||||
typedef struct BACnet_Property_Value {
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t propertyArrayIndex;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
uint8_t priority;
|
||||
/* simple linked list */
|
||||
struct BACnet_Property_Value *next;
|
||||
} BACNET_PROPERTY_VALUE;
|
||||
|
||||
/* used for printing values */
|
||||
struct BACnet_Object_Property_Value;
|
||||
typedef struct BACnet_Object_Property_Value {
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
uint32_t object_instance;
|
||||
BACNET_PROPERTY_ID object_property;
|
||||
uint32_t array_index;
|
||||
BACNET_APPLICATION_DATA_VALUE *value;
|
||||
} BACNET_OBJECT_PROPERTY_VALUE;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bacapp_value_list_init(
|
||||
BACNET_APPLICATION_DATA_VALUE *value,
|
||||
size_t count);
|
||||
void bacapp_property_value_list_init(
|
||||
BACNET_PROPERTY_VALUE *value,
|
||||
size_t count);
|
||||
|
||||
int bacapp_encode_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
int bacapp_decode_data(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_data_type,
|
||||
uint32_t len_value_type,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_decode_application_data(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
bool bacapp_decode_application_data_safe(
|
||||
uint8_t * new_apdu,
|
||||
uint32_t new_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_encode_application_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
int bacapp_decode_context_data(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
int bacapp_encode_context_data(
|
||||
uint8_t * apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
int bacapp_encode_context_data_value(
|
||||
uint8_t * apdu,
|
||||
uint8_t context_tag_number,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
BACNET_APPLICATION_TAG bacapp_context_tag_type(
|
||||
BACNET_PROPERTY_ID property,
|
||||
uint8_t tag_number);
|
||||
|
||||
bool bacapp_copy(
|
||||
BACNET_APPLICATION_DATA_VALUE * dest_value,
|
||||
BACNET_APPLICATION_DATA_VALUE * src_value);
|
||||
|
||||
/* returns the length of data between an opening tag and a closing tag.
|
||||
Expects that the first octet contain the opening tag.
|
||||
Include a value property identifier for context specific data
|
||||
such as the value received in a WriteProperty request */
|
||||
int bacapp_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_PROPERTY_ID property);
|
||||
int bacapp_decode_data_len(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_data_type,
|
||||
uint32_t len_value_type);
|
||||
int bacapp_decode_application_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len);
|
||||
int bacapp_decode_context_data_len(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
BACNET_PROPERTY_ID property);
|
||||
|
||||
#ifndef BACAPP_PRINT_ENABLED
|
||||
#if PRINT_ENABLED || defined TEST
|
||||
#define BACAPP_PRINT_ENABLED
|
||||
#define BACAPP_SNPRINTF_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef BACAPP_SNPRINTF_ENABLED
|
||||
int bacapp_snprintf_value(
|
||||
char *str,
|
||||
size_t str_len,
|
||||
BACNET_OBJECT_PROPERTY_VALUE * object_value);
|
||||
#endif
|
||||
|
||||
#ifdef BACAPP_PRINT_ENABLED
|
||||
bool bacapp_parse_application_data(
|
||||
BACNET_APPLICATION_TAG tag_number,
|
||||
const char *argv,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
bool bacapp_print_value(
|
||||
FILE * stream,
|
||||
BACNET_OBJECT_PROPERTY_VALUE * value);
|
||||
#else
|
||||
/* Provide harmless return values */
|
||||
#define bacapp_parse_application_data(x,y,z) false
|
||||
#define bacapp_print_value(x,y) false
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
#include "bacnet/datetime.h"
|
||||
bool bacapp_same_value(
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_APPLICATION_DATA_VALUE * test_value);
|
||||
|
||||
void testBACnetApplicationDataLength(
|
||||
Test * pTest);
|
||||
void testBACnetApplicationData(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,444 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACDCODE_H
|
||||
#define BACDCODE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacreal.h"
|
||||
#include "bacnet/bits.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* from clause 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool context_specific,
|
||||
uint32_t len_value_type);
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_opening_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
int encode_closing_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
int decode_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t * tag_number);
|
||||
int decode_tag_number_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
uint8_t * tag_number);
|
||||
int decode_tag_number_and_value(
|
||||
uint8_t * apdu,
|
||||
uint8_t * tag_number,
|
||||
uint32_t * value);
|
||||
int decode_tag_number_and_value_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
uint8_t * tag_number,
|
||||
uint32_t * value);
|
||||
/* returns true if the tag is an opening tag and matches */
|
||||
bool decode_is_opening_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
/* returns true if the tag is a closing tag and matches */
|
||||
bool decode_is_closing_tag_number(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
/* returns true if the tag is context specific and matches */
|
||||
bool decode_is_context_tag(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
bool decode_is_context_tag_with_length(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int *tag_length);
|
||||
/* returns true if the tag is an opening tag */
|
||||
bool decode_is_opening_tag(
|
||||
uint8_t * apdu);
|
||||
/* returns true if the tag is a closing tag */
|
||||
bool decode_is_closing_tag(
|
||||
uint8_t * apdu);
|
||||
|
||||
/* from clause 20.2.2 Encoding of a Null Value */
|
||||
int encode_application_null(
|
||||
uint8_t * apdu);
|
||||
int encode_context_null(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number);
|
||||
|
||||
/* from clause 20.2.3 Encoding of a Boolean Value */
|
||||
int encode_application_boolean(
|
||||
uint8_t * apdu,
|
||||
bool boolean_value);
|
||||
bool decode_boolean(
|
||||
uint32_t len_value);
|
||||
int encode_context_boolean(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool boolean_value);
|
||||
bool decode_context_boolean(
|
||||
uint8_t * apdu);
|
||||
|
||||
int decode_context_boolean2(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
bool * boolean_value);
|
||||
|
||||
/* from clause 20.2.10 Encoding of a Bit String Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
|
||||
int decode_context_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bitstring(
|
||||
uint8_t * apdu,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
int encode_application_bitstring(
|
||||
uint8_t * apdu,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
int encode_context_bitstring(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
|
||||
/* from clause 20.2.6 Encoding of a Real Number Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_real(
|
||||
uint8_t * apdu,
|
||||
float value);
|
||||
int encode_context_real(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
float value);
|
||||
|
||||
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_application_double(
|
||||
uint8_t * apdu,
|
||||
double value);
|
||||
|
||||
int encode_context_double(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
double value);
|
||||
|
||||
/* from clause 20.2.14 Encoding of an Object Identifier Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_object_id(
|
||||
uint8_t * apdu,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int decode_object_id_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int decode_context_object_id(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint16_t * object_type,
|
||||
uint32_t * instance);
|
||||
|
||||
int encode_bacnet_object_id(
|
||||
uint8_t * apdu,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
int encode_context_object_id(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
int encode_application_object_id(
|
||||
uint8_t * apdu,
|
||||
int object_type,
|
||||
uint32_t instance);
|
||||
|
||||
/* from clause 20.2.8 Encoding of an Octet String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_octet_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int encode_application_octet_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int encode_context_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int decode_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
int decode_context_octet_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
|
||||
|
||||
/* from clause 20.2.9 Encoding of a Character String Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
uint32_t encode_bacnet_character_string_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t max_apdu,
|
||||
uint8_t encoding,
|
||||
char *pString,
|
||||
uint32_t length);
|
||||
int encode_bacnet_character_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int encode_application_character_string(
|
||||
uint8_t * apdu,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int encode_context_character_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int decode_character_string(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
int decode_context_character_string(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
|
||||
|
||||
/* from clause 20.2.4 Encoding of an Unsigned Integer Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_context_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t value);
|
||||
int encode_application_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int decode_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint32_t * value);
|
||||
int decode_context_unsigned(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t * value);
|
||||
|
||||
/* from clause 20.2.5 Encoding of a Signed Integer Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_signed(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int encode_application_signed(
|
||||
uint8_t * apdu,
|
||||
int32_t value);
|
||||
int encode_context_signed(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int32_t value);
|
||||
int decode_signed(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
int32_t * value);
|
||||
int decode_context_signed(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
int32_t * value);
|
||||
|
||||
|
||||
/* from clause 20.2.11 Encoding of an Enumerated Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
uint32_t * value);
|
||||
int decode_context_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_value,
|
||||
uint32_t * value);
|
||||
int encode_bacnet_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_application_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint32_t value);
|
||||
int encode_context_enumerated(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
uint32_t value);
|
||||
|
||||
/* from clause 20.2.13 Encoding of a Time Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int encode_application_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_bacnet_time_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_TIME * btime);
|
||||
int encode_context_time(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME * btime);
|
||||
int decode_application_time(
|
||||
uint8_t * apdu,
|
||||
BACNET_TIME * btime);
|
||||
int decode_context_bacnet_time(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME * btime);
|
||||
|
||||
|
||||
/* BACnet Date */
|
||||
/* year = years since 1900 */
|
||||
/* month 1=Jan */
|
||||
/* day = day of month */
|
||||
/* wday 1=Monday...7=Sunday */
|
||||
|
||||
/* from clause 20.2.12 Encoding of a Date Value */
|
||||
/* and 20.2.1 General Rules for Encoding BACnet Tags */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int encode_application_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int encode_context_date(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_date_safe(
|
||||
uint8_t * apdu,
|
||||
uint32_t len_value,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_application_date(
|
||||
uint8_t * apdu,
|
||||
BACNET_DATE * bdate);
|
||||
int decode_context_date(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DATE * bdate);
|
||||
|
||||
/* from clause 20.1.2.4 max-segments-accepted */
|
||||
/* and clause 20.1.2.5 max-APDU-length-accepted */
|
||||
/* returns the encoded octet */
|
||||
uint8_t encode_max_segs_max_apdu(
|
||||
int max_segs,
|
||||
int max_apdu);
|
||||
int decode_max_segs(
|
||||
uint8_t octet);
|
||||
int decode_max_apdu(
|
||||
uint8_t octet);
|
||||
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_simple_ack(
|
||||
uint8_t * apdu,
|
||||
uint8_t invoke_id,
|
||||
uint8_t service_choice);
|
||||
|
||||
int encode_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
BACNET_ADDRESS * destination);
|
||||
int decode_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
BACNET_ADDRESS * destination);
|
||||
int encode_context_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ADDRESS * destination);
|
||||
int decode_context_bacnet_address(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_ADDRESS * destination);
|
||||
|
||||
/* from clause 20.2.1.2 Tag Number */
|
||||
/* true if extended tag numbering is used */
|
||||
#define IS_EXTENDED_TAG_NUMBER(x) ((x & 0xF0) == 0xF0)
|
||||
|
||||
/* from clause 20.2.1.3.1 Primitive Data */
|
||||
/* true if the extended value is used */
|
||||
#define IS_EXTENDED_VALUE(x) ((x & 0x07) == 5)
|
||||
|
||||
/* from clause 20.2.1.1 Class */
|
||||
/* true if the tag is context specific */
|
||||
#define IS_CONTEXT_SPECIFIC(x) ((x & BIT3) == BIT3)
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* true if the tag is an opening tag */
|
||||
#define IS_OPENING_TAG(x) ((x & 0x07) == 6)
|
||||
|
||||
/* from clause 20.2.1.3.2 Constructed Data */
|
||||
/* true if the tag is a closing tag */
|
||||
#define IS_CLOSING_TAG(x) ((x & 0x07) == 7)
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void test_BACDCode(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,180 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACDEF_H
|
||||
#define BACDEF_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
/* Silence the warnings about unsafe versions of library functions */
|
||||
/* as we need to keep the code portable */
|
||||
#pragma warning( disable : 4996)
|
||||
#endif
|
||||
|
||||
/* This stack implements this version of BACnet */
|
||||
#define BACNET_PROTOCOL_VERSION 1
|
||||
/* Although this stack can implement a later revision,
|
||||
* sometimes another revision is desired */
|
||||
#ifndef BACNET_PROTOCOL_REVISION
|
||||
#define BACNET_PROTOCOL_REVISION 19
|
||||
#endif
|
||||
|
||||
/* there are a few dependencies on the BACnet Protocol-Revision */
|
||||
#if (BACNET_PROTOCOL_REVISION == 0)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 18
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 35
|
||||
#elif (BACNET_PROTOCOL_REVISION == 1)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 21
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 37
|
||||
#elif (BACNET_PROTOCOL_REVISION == 2)
|
||||
/* from 135-2001 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 23
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 3)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 23
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 4)
|
||||
/* from 135-2004 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 25
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 5)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 30
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 6)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 7)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 8)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 31
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 9)
|
||||
/* from 135-2008 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 38
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 10)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 11)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 12)
|
||||
/* from 135-2010 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 51
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 13)
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 53
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 40
|
||||
#elif (BACNET_PROTOCOL_REVISION == 14) || (BACNET_PROTOCOL_REVISION == 15)
|
||||
/* from 135-2012 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 55
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 16)
|
||||
/* Addendum 135-2012an, 135-2012at, 135-2012au,
|
||||
135-2012av, 135-2012aw, 135-2012ax, 135-2012az */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 56
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 17)
|
||||
/* Addendum 135-2012ai */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 57
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 41
|
||||
#elif (BACNET_PROTOCOL_REVISION == 18) || (BACNET_PROTOCOL_REVISION == 19)
|
||||
/* from 135-2016 version of the BACnet Standard */
|
||||
#define MAX_ASHRAE_OBJECT_TYPE 60
|
||||
#define MAX_BACNET_SERVICES_SUPPORTED 44
|
||||
#else
|
||||
#error MAX_ASHRAE_OBJECT_TYPE and MAX_BACNET_SERVICES_SUPPORTED not defined!
|
||||
#endif
|
||||
|
||||
/* largest BACnet Instance Number */
|
||||
/* Also used as a device instance number wildcard address */
|
||||
#define BACNET_MAX_INSTANCE (0x3FFFFF)
|
||||
#define BACNET_INSTANCE_BITS 22
|
||||
/* large BACnet Object Type */
|
||||
#define BACNET_MAX_OBJECT (0x3FF)
|
||||
/* Array index 0=size of array, n=array element n, MAX=all array elements */
|
||||
/* 32-bit MAX, to use with uint32_t */
|
||||
#define BACNET_ARRAY_ALL 0xFFFFFFFFU
|
||||
/* For device object property references with no device id defined */
|
||||
#define BACNET_NO_DEV_ID 0xFFFFFFFFu
|
||||
#define BACNET_NO_DEV_TYPE 0xFFFFu
|
||||
/* Priority Array for commandable objects */
|
||||
#define BACNET_NO_PRIORITY 0
|
||||
#define BACNET_MIN_PRIORITY 1
|
||||
#define BACNET_MAX_PRIORITY 16
|
||||
|
||||
#define BACNET_BROADCAST_NETWORK (0xFFFF)
|
||||
/* Any size MAC address should be allowed which is less than or
|
||||
equal to 7 bytes. The IPv6 addresses are planned to be handled
|
||||
outside this area. */
|
||||
/* FIXME: mac[] only needs to be as big as our local datalink MAC */
|
||||
#define MAX_MAC_LEN 7
|
||||
struct BACnet_Device_Address {
|
||||
/* mac_len = 0 is a broadcast address */
|
||||
uint8_t mac_len;
|
||||
/* note: MAC for IP addresses uses 4 bytes for addr, 2 bytes for port */
|
||||
/* use de/encode_unsigned32/16 for re/storing the IP address */
|
||||
uint8_t mac[MAX_MAC_LEN];
|
||||
/* DNET,DLEN,DADR or SNET,SLEN,SADR */
|
||||
/* the following are used if the device is behind a router */
|
||||
/* net = 0 indicates local */
|
||||
uint16_t net; /* BACnet network number */
|
||||
/* LEN = 0 denotes broadcast MAC ADR and ADR field is absent */
|
||||
/* LEN > 0 specifies length of ADR field */
|
||||
uint8_t len; /* length of MAC address */
|
||||
uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */
|
||||
};
|
||||
typedef struct BACnet_Device_Address BACNET_ADDRESS;
|
||||
/* define a MAC address for manipulation */
|
||||
struct BACnet_MAC_Address {
|
||||
uint8_t len; /* length of MAC address */
|
||||
uint8_t adr[MAX_MAC_LEN];
|
||||
};
|
||||
typedef struct BACnet_MAC_Address BACNET_MAC_ADDRESS;
|
||||
|
||||
/* note: with microprocessors having lots more code space than memory,
|
||||
it might be better to have a packed encoding with a library to
|
||||
easily access the data. */
|
||||
typedef struct BACnet_Object_Id {
|
||||
uint16_t type;
|
||||
uint32_t instance;
|
||||
} BACNET_OBJECT_ID;
|
||||
|
||||
#define MAX_NPDU (1+1+2+1+MAX_MAC_LEN+2+1+MAX_MAC_LEN+1+1+2)
|
||||
#define MAX_PDU (MAX_APDU + MAX_NPDU)
|
||||
|
||||
#define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) ((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | ((bacnet_object_instance) & BACNET_MAX_INSTANCE))
|
||||
#define BACNET_INSTANCE(bacnet_object_id_num) ((bacnet_object_id_num)&BACNET_MAX_INSTANCE)
|
||||
#define BACNET_TYPE(bacnet_object_id_num) (((bacnet_object_id_num) >> BACNET_INSTANCE_BITS ) & BACNET_MAX_OBJECT)
|
||||
|
||||
#define BACNET_STATUS_OK (0)
|
||||
#define BACNET_STATUS_ERROR (-1)
|
||||
#define BACNET_STATUS_ABORT (-2)
|
||||
#define BACNET_STATUS_REJECT (-3)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,436 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2008 John Minack
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.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,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* BACnetDeviceObjectPropertyReference ::= SEQUENCE {
|
||||
object-identifier [0] BACnetObjectIdentifier,
|
||||
property-identifier [1] BACnetPropertyIdentifier,
|
||||
property-array-index [2] Unsigned OPTIONAL,
|
||||
-- used only with array datatype
|
||||
-- if omitted with an array then
|
||||
-- the entire array is referenced
|
||||
device-identifier [3] BACnetObjectIdentifier OPTIONAL
|
||||
}
|
||||
*/
|
||||
int bacapp_encode_device_obj_property_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
/* object-identifier [0] BACnetObjectIdentifier */
|
||||
len = encode_context_object_id(&apdu[apdu_len], 0,
|
||||
(int)value->objectIdentifier.type, value->objectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
/* property-identifier [1] BACnetPropertyIdentifier */
|
||||
len = encode_context_enumerated(
|
||||
&apdu[apdu_len], 1, value->propertyIdentifier);
|
||||
apdu_len += len;
|
||||
/* property-array-index [2] Unsigned OPTIONAL */
|
||||
/* Check if needed before inserting */
|
||||
if (value->arrayIndex != BACNET_ARRAY_ALL) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 2, value->arrayIndex);
|
||||
apdu_len += len;
|
||||
}
|
||||
/* device-identifier [3] BACnetObjectIdentifier OPTIONAL */
|
||||
/* Likewise, device id is optional so see if needed
|
||||
* (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to
|
||||
* omit */
|
||||
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
|
||||
len = encode_context_object_id(&apdu[apdu_len], 3,
|
||||
(int)value->deviceIdentifier.type,
|
||||
value->deviceIdentifier.instance);
|
||||
apdu_len += len;
|
||||
}
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* BACnetDeviceObjectPropertyReference ::= SEQUENCE {
|
||||
object-identifier [0] BACnetObjectIdentifier,
|
||||
property-identifier [1] BACnetPropertyIdentifier,
|
||||
property-array-index [2] Unsigned OPTIONAL,
|
||||
-- used only with array datatype
|
||||
-- if omitted with an array then
|
||||
-- the entire array is referenced
|
||||
device-identifier [3] BACnetObjectIdentifier OPTIONAL
|
||||
}
|
||||
*/
|
||||
int bacapp_decode_device_obj_property_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
uint32_t enumValue;
|
||||
|
||||
/* object-identifier [0] BACnetObjectIdentifier */
|
||||
if (-1 ==
|
||||
(len = decode_context_object_id(&apdu[apdu_len], 0,
|
||||
&value->objectIdentifier.type,
|
||||
&value->objectIdentifier.instance))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* property-identifier [1] BACnetPropertyIdentifier */
|
||||
if (-1 ==
|
||||
(len = decode_context_enumerated(&apdu[apdu_len], 1, &enumValue))) {
|
||||
return -1;
|
||||
}
|
||||
value->propertyIdentifier = (BACNET_PROPERTY_ID)enumValue;
|
||||
apdu_len += len;
|
||||
/* 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))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
value->arrayIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* 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))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
value->deviceIdentifier.type = BACNET_NO_DEV_TYPE;
|
||||
value->deviceIdentifier.instance = BACNET_NO_DEV_ID;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_device_obj_property_ref(uint8_t *apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
section_length =
|
||||
bacapp_decode_device_obj_property_ref(&apdu[len], value);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* BACnetDeviceObjectReference ::= SEQUENCE {
|
||||
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 len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_device_obj_ref(&apdu[apdu_len], value);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* BACnetDeviceObjectReference ::= SEQUENCE {
|
||||
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
object-identifier [1] BACnetObjectIdentifier
|
||||
}*/
|
||||
int bacapp_encode_device_obj_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
/* device-identifier [0] BACnetObjectIdentifier OPTIONAL */
|
||||
/* Device id is optional so see if needed
|
||||
* (set type to BACNET_NO_DEV_TYPE or something other than OBJECT_DEVICE to
|
||||
* omit */
|
||||
if (value->deviceIdentifier.type == OBJECT_DEVICE) {
|
||||
len = encode_context_object_id(&apdu[apdu_len], 0,
|
||||
(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);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* BACnetDeviceObjectReference ::= SEQUENCE {
|
||||
device-identifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
object-identifier [1] BACnetObjectIdentifier
|
||||
}*/
|
||||
int bacapp_decode_device_obj_ref(
|
||||
uint8_t *apdu, BACNET_DEVICE_OBJECT_REFERENCE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
/* 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))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
value->deviceIdentifier.type = BACNET_NO_DEV_TYPE;
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
section_length = bacapp_decode_device_obj_ref(&apdu[len], value);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
static void testDevObjPropRef(
|
||||
Test *pTest, BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *inData)
|
||||
{
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE outData;
|
||||
uint8_t buffer[MAX_APDU] = { 0 };
|
||||
int inLen = 0;
|
||||
int outLen = 0;
|
||||
|
||||
/* encode */
|
||||
inLen = bacapp_encode_device_obj_property_ref(buffer, inData);
|
||||
/* add a closing tag at the end of the buffer to verify proper handling
|
||||
when that is encountered in real packets */
|
||||
encode_closing_tag(&buffer[inLen], 3);
|
||||
/* 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);
|
||||
ct_test(pTest, inData->propertyIdentifier == outData.propertyIdentifier);
|
||||
if (inData->arrayIndex != BACNET_ARRAY_ALL) {
|
||||
ct_test(pTest, inData->arrayIndex == outData.arrayIndex);
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
ct_test(pTest, outData.deviceIdentifier.instance == BACNET_NO_DEV_ID);
|
||||
ct_test(pTest, outData.deviceIdentifier.type == BACNET_NO_DEV_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
static void testDevIdPropRef(Test *pTest)
|
||||
{
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE inData;
|
||||
|
||||
/* everything encoded */
|
||||
inData.objectIdentifier.instance = 0x1234;
|
||||
inData.objectIdentifier.type = 15;
|
||||
inData.propertyIdentifier = 25;
|
||||
inData.arrayIndex = 0x5678;
|
||||
inData.deviceIdentifier.instance = 0x4343;
|
||||
inData.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
testDevObjPropRef(pTest, &inData);
|
||||
/* optional array */
|
||||
inData.objectIdentifier.instance = 0x1234;
|
||||
inData.objectIdentifier.type = 15;
|
||||
inData.propertyIdentifier = 25;
|
||||
inData.arrayIndex = BACNET_ARRAY_ALL;
|
||||
inData.deviceIdentifier.instance = 0x4343;
|
||||
inData.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
testDevObjPropRef(pTest, &inData);
|
||||
/* optional device ID */
|
||||
inData.objectIdentifier.instance = 0x1234;
|
||||
inData.objectIdentifier.type = 15;
|
||||
inData.propertyIdentifier = 25;
|
||||
inData.arrayIndex = 1;
|
||||
inData.deviceIdentifier.instance = 0;
|
||||
inData.deviceIdentifier.type = BACNET_NO_DEV_TYPE;
|
||||
testDevObjPropRef(pTest, &inData);
|
||||
/* optional array + optional device ID */
|
||||
inData.objectIdentifier.instance = 0x1234;
|
||||
inData.objectIdentifier.type = 15;
|
||||
inData.propertyIdentifier = 25;
|
||||
inData.arrayIndex = BACNET_ARRAY_ALL;
|
||||
inData.deviceIdentifier.instance = 0;
|
||||
inData.deviceIdentifier.type = BACNET_NO_DEV_TYPE;
|
||||
testDevObjPropRef(pTest, &inData);
|
||||
}
|
||||
|
||||
static void testDevIdRef(Test *pTest)
|
||||
{
|
||||
BACNET_DEVICE_OBJECT_REFERENCE inData;
|
||||
BACNET_DEVICE_OBJECT_REFERENCE outData;
|
||||
uint8_t buffer[MAX_APDU];
|
||||
int inLen;
|
||||
int outLen;
|
||||
|
||||
inData.deviceIdentifier.instance = 0x4343;
|
||||
inData.deviceIdentifier.type = OBJECT_DEVICE;
|
||||
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);
|
||||
}
|
||||
|
||||
void testBACnetDeviceObjectPropertyReference(Test *pTest)
|
||||
{
|
||||
bool rc;
|
||||
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testDevIdPropRef);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testDevIdRef);
|
||||
assert(rc);
|
||||
}
|
||||
|
||||
#ifdef TEST_DEV_ID_PROP_REF
|
||||
#include <assert.h>
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
|
||||
pTest = ct_create("BACnet Prop Ref", NULL);
|
||||
testBACnetDeviceObjectPropertyReference(pTest);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_DEV_ID_PROP_REF */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,101 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 John Minack
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef _BAC_DEV_PROP_REF_H_
|
||||
#define _BAC_DEV_PROP_REF_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
|
||||
typedef struct BACnetDeviceObjectPropertyReference {
|
||||
BACNET_OBJECT_ID objectIdentifier;
|
||||
BACNET_PROPERTY_ID propertyIdentifier;
|
||||
uint32_t arrayIndex;
|
||||
BACNET_OBJECT_ID deviceIdentifier;
|
||||
} BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE;
|
||||
|
||||
/** BACnetDeviceObjectReference structure.
|
||||
* If the optional deviceIdentifier is not provided, then this refers
|
||||
* to an object inside this Device.
|
||||
*/
|
||||
typedef struct BACnetDeviceObjectReference {
|
||||
BACNET_OBJECT_ID deviceIdentifier; /**< Optional, for external device. */
|
||||
BACNET_OBJECT_ID objectIdentifier;
|
||||
} BACNET_DEVICE_OBJECT_REFERENCE;
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_encode_context_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_context_device_obj_property_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
|
||||
|
||||
|
||||
int bacapp_encode_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_encode_context_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
int bacapp_decode_context_device_obj_ref(
|
||||
uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_DEVICE_OBJECT_REFERENCE * value);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBACnetDeviceObjectPropertyReference(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,258 @@
|
||||
/*####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####*/
|
||||
#include <stdint.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 apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_ERROR;
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = service;
|
||||
apdu_len = 3;
|
||||
/* service parameters */
|
||||
apdu_len += encode_application_enumerated(&apdu[apdu_len], error_class);
|
||||
apdu_len += encode_application_enumerated(&apdu[apdu_len], error_code);
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
#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 len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
uint32_t decoded_value = 0;
|
||||
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
len += decode_enumerated(&apdu[len], len_value_type, &decoded_value);
|
||||
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 len = 0;
|
||||
|
||||
if (apdu_len > 2) {
|
||||
if (invoke_id) {
|
||||
*invoke_id = apdu[0];
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#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 len = 0;
|
||||
|
||||
if (!apdu)
|
||||
return -1;
|
||||
/* optional checking - most likely was already done prior to this call */
|
||||
if (apdu_len) {
|
||||
if (apdu[0] != PDU_TYPE_ERROR)
|
||||
return -1;
|
||||
if (apdu_len > 1) {
|
||||
len = bacerror_decode_service_request(&apdu[1], apdu_len - 1,
|
||||
invoke_id, service, error_class, error_code);
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void testBACError(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[480] = { 0 };
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
BACNET_CONFIRMED_SERVICE service = 0;
|
||||
BACNET_ERROR_CLASS error_class = 0;
|
||||
BACNET_ERROR_CODE error_code = 0;
|
||||
uint8_t test_invoke_id = 0;
|
||||
BACNET_CONFIRMED_SERVICE test_service = 0;
|
||||
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);
|
||||
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);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
ct_test(pTest, test_error_class == error_class);
|
||||
ct_test(pTest, test_error_code == error_code);
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
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);
|
||||
ct_test(pTest, len == 0);
|
||||
|
||||
/* check them all... */
|
||||
for (service = 0; service < MAX_BACNET_CONFIRMED_SERVICE; service++) {
|
||||
for (error_class = 0; error_class < ERROR_CLASS_PROPRIETARY_FIRST;
|
||||
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);
|
||||
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);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
ct_test(pTest, test_error_class == error_class);
|
||||
ct_test(pTest, test_error_code == error_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* max boundaries */
|
||||
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);
|
||||
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);
|
||||
ct_test(pTest, len != -1);
|
||||
ct_test(pTest, test_invoke_id == invoke_id);
|
||||
ct_test(pTest, test_service == service);
|
||||
ct_test(pTest, test_error_class == error_class);
|
||||
ct_test(pTest, test_error_code == error_code);
|
||||
}
|
||||
|
||||
#ifdef TEST_BACERROR
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Error", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBACError);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ERROR */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,409 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2004 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####*/
|
||||
|
||||
/* BACnet Integer encoding and decoding */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacint.h"
|
||||
|
||||
/** @file bacint.c Encode/Decode Integer Types */
|
||||
|
||||
int encode_unsigned16(uint8_t *apdu, uint16_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff00) >> 8);
|
||||
apdu[1] = (uint8_t)(value & 0x00ff);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int decode_unsigned16(uint8_t *apdu, uint16_t *value)
|
||||
{
|
||||
if (value) {
|
||||
*value = (uint16_t)((((uint16_t)apdu[0]) << 8) & 0xff00);
|
||||
*value |= ((uint16_t)(((uint16_t)apdu[1]) & 0x00ff));
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int encode_unsigned24(uint8_t *apdu, uint32_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff0000) >> 16);
|
||||
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
|
||||
apdu[2] = (uint8_t)(value & 0x0000ff);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
int decode_unsigned24(uint8_t *apdu, uint32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
*value = ((uint32_t)((((uint32_t)apdu[0]) << 16) & 0x00ff0000));
|
||||
*value |= (uint32_t)((((uint32_t)apdu[1]) << 8) & 0x0000ff00);
|
||||
*value |= ((uint32_t)(((uint32_t)apdu[2]) & 0x000000ff));
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
int encode_unsigned32(uint8_t *apdu, uint32_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff000000) >> 24);
|
||||
apdu[1] = (uint8_t)((value & 0x00ff0000) >> 16);
|
||||
apdu[2] = (uint8_t)((value & 0x0000ff00) >> 8);
|
||||
apdu[3] = (uint8_t)(value & 0x000000ff);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
int decode_unsigned32(uint8_t *apdu, uint32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
*value = ((uint32_t)((((uint32_t)apdu[0]) << 24) & 0xff000000));
|
||||
*value |= ((uint32_t)((((uint32_t)apdu[1]) << 16) & 0x00ff0000));
|
||||
*value |= ((uint32_t)((((uint32_t)apdu[2]) << 8) & 0x0000ff00));
|
||||
*value |= ((uint32_t)(((uint32_t)apdu[3]) & 0x000000ff));
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
#ifdef UINT64_MAX
|
||||
/**
|
||||
* @brief Encode a 64-bit unsigned value into buffer
|
||||
* @param buffer - pointer to bytes for storing encoding
|
||||
* @param value - 16-bit value to encode
|
||||
* @return Returns the number of bytes encoded
|
||||
*/
|
||||
int encode_unsigned64(uint8_t *buffer, uint64_t value)
|
||||
{
|
||||
buffer[0] = (uint8_t)((value & 0xff00000000000000) >> 56);
|
||||
buffer[1] = (uint8_t)((value & 0x00ff000000000000) >> 48);
|
||||
buffer[2] = (uint8_t)((value & 0x0000ff0000000000) >> 40);
|
||||
buffer[3] = (uint8_t)((value & 0x000000ff00000000) >> 32);
|
||||
buffer[4] = (uint8_t)((value & 0x00000000ff000000) >> 24);
|
||||
buffer[5] = (uint8_t)((value & 0x0000000000ff0000) >> 16);
|
||||
buffer[6] = (uint8_t)((value & 0x000000000000ff00) >> 8);
|
||||
buffer[7] = (uint8_t)(value & 0x00000000000000ff);
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode a 64-bit unsigned value from a buffer
|
||||
* @param buffer - pointer to bytes for used for decoding
|
||||
* @param value - pointer to 64-bit value to store the decoded value
|
||||
* @return Returns the number of bytes decoded
|
||||
*/
|
||||
int decode_unsigned64(uint8_t *buffer, uint64_t *value)
|
||||
{
|
||||
if (value) {
|
||||
*value =
|
||||
((uint64_t)((((uint64_t)buffer[0]) << 56) & 0xff00000000000000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[1]) << 48) & 0x00ff000000000000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[2]) << 40) & 0x0000ff0000000000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[3]) << 32) & 0x000000ff00000000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[4]) << 24) & 0x00000000ff000000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[5]) << 16) & 0x0000000000ff0000));
|
||||
*value |=
|
||||
((uint64_t)((((uint64_t)buffer[6]) << 8) & 0x000000000000ff00));
|
||||
*value |= ((uint64_t)(((uint64_t)buffer[7]) & 0x00000000000000ff));
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if BACNET_USE_SIGNED
|
||||
int encode_signed8(uint8_t *apdu, int8_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int decode_signed8(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFFFFFF00;
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
*value |= ((int32_t)(((int32_t)apdu[0]) & 0x000000ff));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int encode_signed16(uint8_t *apdu, int16_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff00) >> 8);
|
||||
apdu[1] = (uint8_t)(value & 0x00ff);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int decode_signed16(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFFFF0000;
|
||||
} else {
|
||||
*value = 0;
|
||||
}
|
||||
*value |= ((int32_t)((((int32_t)apdu[0]) << 8) & 0x0000ff00));
|
||||
*value |= ((int32_t)(((int32_t)apdu[1]) & 0x000000ff));
|
||||
}
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
int encode_signed24(uint8_t *apdu, int32_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff0000) >> 16);
|
||||
apdu[1] = (uint8_t)((value & 0x00ff00) >> 8);
|
||||
apdu[2] = (uint8_t)(value & 0x0000ff);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
int decode_signed24(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
/* negative - bit 7 is set */
|
||||
if (apdu[0] & 0x80) {
|
||||
*value = 0xFF000000;
|
||||
} 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));
|
||||
}
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
int encode_signed32(uint8_t *apdu, int32_t value)
|
||||
{
|
||||
apdu[0] = (uint8_t)((value & 0xff000000) >> 24);
|
||||
apdu[1] = (uint8_t)((value & 0x00ff0000) >> 16);
|
||||
apdu[2] = (uint8_t)((value & 0x0000ff00) >> 8);
|
||||
apdu[3] = (uint8_t)(value & 0x000000ff);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
int decode_signed32(uint8_t *apdu, int32_t *value)
|
||||
{
|
||||
if (value) {
|
||||
*value = ((int32_t)((((int32_t)apdu[0]) << 24) & 0xff000000));
|
||||
*value |= ((int32_t)((((int32_t)apdu[1]) << 16) & 0x00ff0000));
|
||||
*value |= ((int32_t)((((int32_t)apdu[2]) << 8) & 0x0000ff00));
|
||||
*value |= ((int32_t)(((int32_t)apdu[3]) & 0x000000ff));
|
||||
}
|
||||
|
||||
return 4;
|
||||
}
|
||||
#endif
|
||||
/* end of decoding_encoding.c */
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "ctest.h"
|
||||
|
||||
static void testBACnetUnsigned16(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint16_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = 0;; value++) {
|
||||
len = encode_unsigned16(&apdu[0], value);
|
||||
ct_test(pTest, len == 2);
|
||||
len = decode_unsigned16(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
if (value == 0xFFFF)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetUnsigned24(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = 0;; value += 0xf) {
|
||||
len = encode_unsigned24(&apdu[0], value);
|
||||
ct_test(pTest, len == 3);
|
||||
len = decode_unsigned24(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
if (value == 0xffffff)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetUnsigned32(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
uint32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = 0;; value += 0xff) {
|
||||
len = encode_unsigned32(&apdu[0], value);
|
||||
ct_test(pTest, len == 4);
|
||||
len = decode_unsigned32(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
if (value == 0xffffffff)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetSigned8(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = -127;; value++) {
|
||||
len = encode_signed8(&apdu[0], value);
|
||||
ct_test(pTest, len == 1);
|
||||
len = decode_signed8(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
if (value == 127)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetSigned16(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = -32767;; value++) {
|
||||
len = encode_signed16(&apdu[0], value);
|
||||
ct_test(pTest, len == 2);
|
||||
len = decode_signed16(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
if (value == 32767)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetSigned24(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = -8388607; value <= 8388607; value += 15) {
|
||||
len = encode_signed24(&apdu[0], value);
|
||||
ct_test(pTest, len == 3);
|
||||
len = decode_signed24(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
}
|
||||
}
|
||||
|
||||
static void testBACnetSigned32(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[32] = { 0 };
|
||||
int32_t value = 0, test_value = 0;
|
||||
int len = 0;
|
||||
|
||||
for (value = -2147483647; value < 0; value += 127) {
|
||||
len = encode_signed32(&apdu[0], value);
|
||||
ct_test(pTest, len == 4);
|
||||
len = decode_signed32(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
}
|
||||
for (value = 2147483647; value > 0; value -= 127) {
|
||||
len = encode_signed32(&apdu[0], value);
|
||||
ct_test(pTest, len == 4);
|
||||
len = decode_signed32(&apdu[0], &test_value);
|
||||
ct_test(pTest, value == test_value);
|
||||
}
|
||||
}
|
||||
|
||||
void testBACnetIntegers(Test *pTest)
|
||||
{
|
||||
bool rc;
|
||||
|
||||
assert(pTest);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBACnetUnsigned16);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetUnsigned24);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetUnsigned32);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetSigned8);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetSigned16);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetSigned24);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACnetSigned32);
|
||||
assert(rc);
|
||||
}
|
||||
|
||||
#ifdef TEST_BACINT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
|
||||
pTest = ct_create("BACint", NULL);
|
||||
testBACnetIntegers(pTest);
|
||||
/* configure output */
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BACINT */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,97 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 John Goulah
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#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 }
|
||||
};
|
||||
|
||||
signed bacprop_tag_by_index_default(
|
||||
PROP_TAG_DATA *data_list, signed index, signed default_ret)
|
||||
{
|
||||
signed pUnsigned = 0;
|
||||
|
||||
if (data_list) {
|
||||
while (data_list->prop_id != -1) {
|
||||
if (data_list->prop_id == index) {
|
||||
pUnsigned = data_list->tag_id;
|
||||
break;
|
||||
}
|
||||
data_list++;
|
||||
}
|
||||
}
|
||||
|
||||
return pUnsigned ? pUnsigned : default_ret;
|
||||
}
|
||||
|
||||
signed bacprop_property_tag(BACNET_OBJECT_TYPE type, signed prop)
|
||||
{
|
||||
switch (type) {
|
||||
case OBJECT_DEVICE:
|
||||
return bacprop_tag_by_index_default(
|
||||
bacnet_object_device_property_tag_map, prop, -1);
|
||||
default:
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Unsupported object type");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,422 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2008 John Minack
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <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 */
|
||||
|
||||
int bacapp_decode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t len_value_type;
|
||||
int section_length;
|
||||
uint32_t enumValue;
|
||||
uint8_t tagnum;
|
||||
|
||||
section_length =
|
||||
decode_tag_number_and_value(&apdu[len], &tagnum, &len_value_type);
|
||||
|
||||
if (-1 == section_length) {
|
||||
return -1;
|
||||
}
|
||||
value->tag = tagnum;
|
||||
len += section_length;
|
||||
switch (value->tag) {
|
||||
case BOOLEAN_VALUE:
|
||||
value->state.booleanValue = decode_boolean(len_value_type);
|
||||
break;
|
||||
|
||||
case BINARY_VALUE:
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.eventType = (BACNET_EVENT_TYPE)enumValue;
|
||||
break;
|
||||
|
||||
case POLARITY:
|
||||
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))) {
|
||||
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))) {
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.programError = (BACNET_PROGRAM_ERROR)enumValue;
|
||||
break;
|
||||
|
||||
case RELIABILITY:
|
||||
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))) {
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.systemStatus = (BACNET_DEVICE_STATUS)enumValue;
|
||||
break;
|
||||
|
||||
case UNITS:
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_MODE:
|
||||
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))) {
|
||||
return -1;
|
||||
}
|
||||
value->state.lifeSafetyState = (BACNET_LIFE_SAFETY_STATE)enumValue;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int bacapp_decode_context_property_state(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_PROPERTY_STATE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
section_length = bacapp_decode_property_state(&apdu[len], value);
|
||||
|
||||
if (section_length == -1) {
|
||||
len = -1;
|
||||
} else {
|
||||
len += section_length;
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int bacapp_encode_property_state(uint8_t *apdu, BACNET_PROPERTY_STATE *value)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
if (value && apdu) {
|
||||
switch (value->tag) {
|
||||
case BOOLEAN_VALUE:
|
||||
len = encode_context_boolean(
|
||||
&apdu[0], 0, value->state.booleanValue);
|
||||
break;
|
||||
|
||||
case BINARY_VALUE:
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 1, value->state.binaryValue);
|
||||
break;
|
||||
|
||||
case EVENT_TYPE:
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 2, value->state.eventType);
|
||||
break;
|
||||
|
||||
case 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);
|
||||
break;
|
||||
|
||||
case PROGRAM_STATE:
|
||||
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);
|
||||
break;
|
||||
|
||||
case RELIABILITY:
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 7, value->state.reliability);
|
||||
break;
|
||||
|
||||
case STATE:
|
||||
len =
|
||||
encode_context_enumerated(&apdu[0], 8, value->state.state);
|
||||
break;
|
||||
|
||||
case SYSTEM_STATUS:
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 9, value->state.systemStatus);
|
||||
break;
|
||||
|
||||
case UNITS:
|
||||
len =
|
||||
encode_context_enumerated(&apdu[0], 10, value->state.units);
|
||||
break;
|
||||
|
||||
case UNSIGNED_VALUE:
|
||||
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);
|
||||
break;
|
||||
|
||||
case LIFE_SAFETY_STATE:
|
||||
len = encode_context_enumerated(
|
||||
&apdu[0], 13, value->state.lifeSafetyState);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* FIXME: assert(0); - return a negative len? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <string.h> /* for memset */
|
||||
|
||||
void testPropStates(Test *pTest)
|
||||
{
|
||||
BACNET_PROPERTY_STATE inData;
|
||||
BACNET_PROPERTY_STATE outData;
|
||||
uint8_t appMsg[MAX_APDU];
|
||||
int inLen;
|
||||
int outLen;
|
||||
|
||||
inData.tag = BOOLEAN_VALUE;
|
||||
inData.state.booleanValue = true;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.booleanValue == outData.state.booleanValue);
|
||||
|
||||
/****************
|
||||
*****************
|
||||
****************/
|
||||
inData.tag = BINARY_VALUE;
|
||||
inData.state.binaryValue = BINARY_ACTIVE;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.binaryValue == outData.state.binaryValue);
|
||||
|
||||
/****************
|
||||
*****************
|
||||
****************/
|
||||
inData.tag = EVENT_TYPE;
|
||||
inData.state.eventType = EVENT_BUFFER_READY;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.eventType == outData.state.eventType);
|
||||
|
||||
/****************
|
||||
*****************
|
||||
****************/
|
||||
inData.tag = POLARITY;
|
||||
inData.state.polarity = POLARITY_REVERSE;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.polarity == outData.state.polarity);
|
||||
|
||||
/****************
|
||||
*****************
|
||||
****************/
|
||||
inData.tag = PROGRAM_CHANGE;
|
||||
inData.state.programChange = PROGRAM_REQUEST_RESTART;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.programChange == outData.state.programChange);
|
||||
|
||||
/****************
|
||||
*****************
|
||||
****************/
|
||||
inData.tag = UNSIGNED_VALUE;
|
||||
inData.state.unsignedValue = 0xdeadbeef;
|
||||
|
||||
inLen = bacapp_encode_property_state(appMsg, &inData);
|
||||
|
||||
memset(&outData, 0, sizeof(outData));
|
||||
|
||||
outLen = bacapp_decode_property_state(appMsg, &outData);
|
||||
|
||||
ct_test(pTest, outLen == inLen);
|
||||
ct_test(pTest, inData.tag == outData.tag);
|
||||
ct_test(pTest, inData.state.unsignedValue == outData.state.unsignedValue);
|
||||
}
|
||||
|
||||
#ifdef TEST_PROP_STATES
|
||||
#include <assert.h>
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Event", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testPropStates);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* TEST_PROP_STATES */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
@@ -0,0 +1,282 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2004-2008 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <string.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 */
|
||||
|
||||
/* NOTE: byte order plays a role in decoding multibyte values */
|
||||
/* http://www.unixpapa.com/incnote/byteorder.html */
|
||||
#ifndef BIG_ENDIAN
|
||||
#error Define BIG_ENDIAN=0 or BIG_ENDIAN=1 for BACnet Stack in compiler settings
|
||||
#endif
|
||||
|
||||
/* from clause 20.2.6 Encoding of a Real Number Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_real(uint8_t *apdu, float *real_value)
|
||||
{
|
||||
union {
|
||||
uint8_t byte[4];
|
||||
float real_value;
|
||||
} my_data;
|
||||
|
||||
/* NOTE: assumes the compiler stores float as IEEE-754 float */
|
||||
#if BIG_ENDIAN
|
||||
my_data.byte[0] = apdu[0];
|
||||
my_data.byte[1] = apdu[1];
|
||||
my_data.byte[2] = apdu[2];
|
||||
my_data.byte[3] = apdu[3];
|
||||
#else
|
||||
my_data.byte[0] = apdu[3];
|
||||
my_data.byte[1] = apdu[2];
|
||||
my_data.byte[2] = apdu[1];
|
||||
my_data.byte[3] = apdu[0];
|
||||
#endif
|
||||
|
||||
*real_value = my_data.real_value;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
int decode_real_safe(uint8_t *apdu, uint32_t len_value, float *real_value)
|
||||
{
|
||||
if (len_value != 4) {
|
||||
*real_value = 0.0f;
|
||||
return (int)len_value;
|
||||
} else {
|
||||
return decode_real(apdu, real_value);
|
||||
}
|
||||
}
|
||||
|
||||
int decode_context_real(uint8_t *apdu, uint8_t tag_number, float *real_value)
|
||||
{
|
||||
uint32_t len_value;
|
||||
int len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[len], tag_number)) {
|
||||
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
|
||||
len += decode_real(&apdu[len], real_value);
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/* from clause 20.2.6 Encoding of a Real Number Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_real(float value, uint8_t *apdu)
|
||||
{
|
||||
union {
|
||||
uint8_t byte[4];
|
||||
float real_value;
|
||||
} my_data;
|
||||
|
||||
/* NOTE: assumes the compiler stores float as IEEE-754 float */
|
||||
my_data.real_value = value;
|
||||
#if BIG_ENDIAN
|
||||
apdu[0] = my_data.byte[0];
|
||||
apdu[1] = my_data.byte[1];
|
||||
apdu[2] = my_data.byte[2];
|
||||
apdu[3] = my_data.byte[3];
|
||||
#else
|
||||
apdu[0] = my_data.byte[3];
|
||||
apdu[1] = my_data.byte[2];
|
||||
apdu[2] = my_data.byte[1];
|
||||
apdu[3] = my_data.byte[0];
|
||||
#endif
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
#if BACNET_USE_DOUBLE
|
||||
|
||||
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int decode_double(uint8_t *apdu, double *double_value)
|
||||
{
|
||||
union {
|
||||
uint8_t byte[8];
|
||||
double double_value;
|
||||
} my_data;
|
||||
|
||||
/* NOTE: assumes the compiler stores float as IEEE-754 float */
|
||||
#if BIG_ENDIAN
|
||||
my_data.byte[0] = apdu[0];
|
||||
my_data.byte[1] = apdu[1];
|
||||
my_data.byte[2] = apdu[2];
|
||||
my_data.byte[3] = apdu[3];
|
||||
my_data.byte[4] = apdu[4];
|
||||
my_data.byte[5] = apdu[5];
|
||||
my_data.byte[6] = apdu[6];
|
||||
my_data.byte[7] = apdu[7];
|
||||
#else
|
||||
my_data.byte[0] = apdu[7];
|
||||
my_data.byte[1] = apdu[6];
|
||||
my_data.byte[2] = apdu[5];
|
||||
my_data.byte[3] = apdu[4];
|
||||
my_data.byte[4] = apdu[3];
|
||||
my_data.byte[5] = apdu[2];
|
||||
my_data.byte[6] = apdu[1];
|
||||
my_data.byte[7] = apdu[0];
|
||||
#endif
|
||||
|
||||
*double_value = my_data.double_value;
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
int decode_double_safe(uint8_t *apdu, uint32_t len_value, double *double_value)
|
||||
{
|
||||
if (len_value != 8) {
|
||||
*double_value = 0.0;
|
||||
return (int)len_value;
|
||||
} else {
|
||||
return decode_double(apdu, double_value);
|
||||
}
|
||||
}
|
||||
|
||||
/* from clause 20.2.7 Encoding of a Double Precision Real Number Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
int encode_bacnet_double(double value, uint8_t *apdu)
|
||||
{
|
||||
union {
|
||||
uint8_t byte[8];
|
||||
double double_value;
|
||||
} my_data;
|
||||
|
||||
/* NOTE: assumes the compiler stores float as IEEE-754 float */
|
||||
my_data.double_value = value;
|
||||
#if BIG_ENDIAN
|
||||
apdu[0] = my_data.byte[0];
|
||||
apdu[1] = my_data.byte[1];
|
||||
apdu[2] = my_data.byte[2];
|
||||
apdu[3] = my_data.byte[3];
|
||||
apdu[4] = my_data.byte[4];
|
||||
apdu[5] = my_data.byte[5];
|
||||
apdu[6] = my_data.byte[6];
|
||||
apdu[7] = my_data.byte[7];
|
||||
#else
|
||||
apdu[0] = my_data.byte[7];
|
||||
apdu[1] = my_data.byte[6];
|
||||
apdu[2] = my_data.byte[5];
|
||||
apdu[3] = my_data.byte[4];
|
||||
apdu[4] = my_data.byte[3];
|
||||
apdu[5] = my_data.byte[2];
|
||||
apdu[6] = my_data.byte[1];
|
||||
apdu[7] = my_data.byte[0];
|
||||
#endif
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
int decode_context_double(
|
||||
uint8_t *apdu, uint8_t tag_number, double *double_value)
|
||||
{
|
||||
uint32_t len_value;
|
||||
int len = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[len], tag_number)) {
|
||||
len += decode_tag_number_and_value(&apdu[len], &tag_number, &len_value);
|
||||
len += decode_double(&apdu[len], double_value);
|
||||
} else {
|
||||
len = -1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* end of decoding_encoding.c */
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testBACreal(Test *pTest)
|
||||
{
|
||||
float real_value = 3.14159F, test_real_value = 0.0;
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
len = encode_bacnet_real(real_value, &apdu[0]);
|
||||
ct_test(pTest, len == 4);
|
||||
test_len = decode_real(&apdu[0], &test_real_value);
|
||||
ct_test(pTest, test_len == len);
|
||||
ct_test(pTest, test_real_value == real_value);
|
||||
}
|
||||
|
||||
void testBACdouble(Test *pTest)
|
||||
{
|
||||
double double_value = 3.1415927, test_double_value = 0.0;
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0, test_len = 0;
|
||||
|
||||
len = encode_bacnet_double(double_value, &apdu[0]);
|
||||
ct_test(pTest, len == 8);
|
||||
test_len = decode_double(&apdu[0], &test_double_value);
|
||||
ct_test(pTest, test_len == len);
|
||||
ct_test(pTest, test_double_value == double_value);
|
||||
}
|
||||
|
||||
#ifdef TEST_BACNET_REAL
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACreal", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBACreal);
|
||||
assert(rc);
|
||||
rc = ct_addTestFunction(pTest, testBACdouble);
|
||||
assert(rc);
|
||||
|
||||
/* configure output */
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BACNET_REAL */
|
||||
#endif /* TEST */
|
||||
@@ -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
|
||||
+1056
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,207 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACSTR_H
|
||||
#define BACSTR_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/config.h"
|
||||
|
||||
/* bit strings
|
||||
They could be as large as 256/8=32 octets */
|
||||
typedef struct BACnet_Bit_String {
|
||||
uint8_t bits_used;
|
||||
uint8_t value[MAX_BITSTRING_BYTES];
|
||||
} BACNET_BIT_STRING;
|
||||
|
||||
typedef struct BACnet_Character_String {
|
||||
size_t length;
|
||||
uint8_t encoding;
|
||||
/* limit - 6 octets is the most our tag and type could be */
|
||||
char value[MAX_CHARACTER_STRING_BYTES];
|
||||
} BACNET_CHARACTER_STRING;
|
||||
|
||||
/* FIXME: convert the bacdcode library to use BACNET_OCTET_STRING
|
||||
for APDU buffer to prevent buffer overflows */
|
||||
typedef struct BACnet_Octet_String {
|
||||
size_t length;
|
||||
/* limit - 6 octets is the most our tag and type could be */
|
||||
uint8_t value[MAX_OCTET_STRING_BYTES];
|
||||
} BACNET_OCTET_STRING;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void bitstring_init(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
void bitstring_set_bit(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bit_number,
|
||||
bool value);
|
||||
bool bitstring_bit(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bit_number);
|
||||
uint8_t bitstring_bits_used(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* returns the number of bytes that a bit string is using */
|
||||
uint8_t bitstring_bytes_used(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
uint8_t bitstring_bits_capacity(
|
||||
BACNET_BIT_STRING * bit_string);
|
||||
/* used for encoding and decoding from the APDU */
|
||||
uint8_t bitstring_octet(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t octet_index);
|
||||
bool bitstring_set_octet(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t index,
|
||||
uint8_t octet);
|
||||
bool bitstring_set_bits_used(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
uint8_t bytes_used,
|
||||
uint8_t unused_bits);
|
||||
bool bitstring_copy(
|
||||
BACNET_BIT_STRING * dest,
|
||||
BACNET_BIT_STRING * src);
|
||||
bool bitstring_same(
|
||||
BACNET_BIT_STRING * bitstring1,
|
||||
BACNET_BIT_STRING * bitstring2);
|
||||
bool bitstring_init_ascii(
|
||||
BACNET_BIT_STRING * bit_string,
|
||||
const char *ascii);
|
||||
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using length=0 */
|
||||
bool characterstring_init(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
uint8_t encoding,
|
||||
const char *value,
|
||||
size_t length);
|
||||
/* used for ANSI C-Strings */
|
||||
bool characterstring_init_ansi(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
const char *value);
|
||||
bool characterstring_copy(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
bool characterstring_ansi_copy(
|
||||
char *dest,
|
||||
size_t dest_max_len,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
/* returns true if the strings are the same length, encoding, value */
|
||||
bool characterstring_same(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
BACNET_CHARACTER_STRING * src);
|
||||
bool characterstring_ansi_same(
|
||||
BACNET_CHARACTER_STRING * dest,
|
||||
const char *src);
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool characterstring_append(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
const char *value,
|
||||
size_t length);
|
||||
/* This function sets a new length without changing the value.
|
||||
If length exceeds capacity, no modification happens and
|
||||
function returns false. */
|
||||
bool characterstring_truncate(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
size_t length);
|
||||
bool characterstring_set_encoding(
|
||||
BACNET_CHARACTER_STRING * char_string,
|
||||
uint8_t encoding);
|
||||
/* Returns the value */
|
||||
char *characterstring_value(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
/* returns the length */
|
||||
size_t characterstring_length(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
uint8_t characterstring_encoding(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
size_t characterstring_capacity(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool characterstring_printable(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool characterstring_valid(
|
||||
BACNET_CHARACTER_STRING * char_string);
|
||||
bool utf8_isvalid(
|
||||
const char *str,
|
||||
size_t length);
|
||||
|
||||
/* returns false if the string exceeds capacity
|
||||
initialize by using length=0 */
|
||||
bool octetstring_init(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
uint8_t * value,
|
||||
size_t length);
|
||||
#ifdef PRINT_ENABLED
|
||||
/* converts an null terminated ASCII Hex string to an octet string.
|
||||
returns true if successfully converted and fits; false if too long */
|
||||
bool octetstring_init_ascii_hex(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
const char *ascii_hex);
|
||||
#endif
|
||||
bool octetstring_copy(
|
||||
BACNET_OCTET_STRING * dest,
|
||||
BACNET_OCTET_STRING * src);
|
||||
size_t octetstring_copy_value(
|
||||
uint8_t * dest,
|
||||
size_t length,
|
||||
BACNET_OCTET_STRING * src);
|
||||
/* returns false if the string exceeds capacity */
|
||||
bool octetstring_append(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
uint8_t * value,
|
||||
size_t length);
|
||||
/* This function sets a new length without changing the value.
|
||||
If length exceeds capacity, no modification happens and
|
||||
function returns false. */
|
||||
bool octetstring_truncate(
|
||||
BACNET_OCTET_STRING * octet_string,
|
||||
size_t length);
|
||||
/* Returns the value */
|
||||
uint8_t *octetstring_value(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
/* Returns the length.*/
|
||||
size_t octetstring_length(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
size_t octetstring_capacity(
|
||||
BACNET_OCTET_STRING * octet_string);
|
||||
/* returns true if the same length and contents */
|
||||
bool octetstring_value_same(
|
||||
BACNET_OCTET_STRING * octet_string1,
|
||||
BACNET_OCTET_STRING * octet_string2);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBACnetStrings(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef BACTEXT_H
|
||||
#define BACTEXT_H
|
||||
|
||||
/* tiny implementations have no need to print */
|
||||
#if PRINT_ENABLED
|
||||
#define BACTEXT_PRINT_ENABLED
|
||||
#else
|
||||
#ifdef TEST
|
||||
#define BACTEXT_PRINT_ENABLED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/indtext.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
const char *bactext_confirmed_service_name(
|
||||
unsigned index);
|
||||
const char *bactext_unconfirmed_service_name(
|
||||
unsigned index);
|
||||
const char *bactext_application_tag_name(
|
||||
unsigned index);
|
||||
bool bactext_application_tag_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_object_type_name(
|
||||
unsigned index);
|
||||
bool bactext_object_type_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_notify_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_property_name(
|
||||
unsigned index);
|
||||
const char *bactext_property_name_default(
|
||||
unsigned index,
|
||||
const char *default_string);
|
||||
bool bactext_property_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_engineering_unit_name(
|
||||
unsigned index);
|
||||
bool bactext_engineering_unit_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_reject_reason_name(
|
||||
unsigned index);
|
||||
const char *bactext_abort_reason_name(
|
||||
unsigned index);
|
||||
const char *bactext_error_class_name(
|
||||
unsigned index);
|
||||
const char *bactext_error_code_name(
|
||||
unsigned index);
|
||||
unsigned bactext_property_id(
|
||||
const char *name);
|
||||
const char *bactext_month_name(
|
||||
unsigned index);
|
||||
const char *bactext_week_of_month_name(
|
||||
unsigned index);
|
||||
const char *bactext_day_of_week_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_state_name(
|
||||
unsigned index);
|
||||
const char *bactext_binary_present_value_name(
|
||||
unsigned index);
|
||||
const char *bactext_binary_polarity_name(
|
||||
unsigned index);
|
||||
bool bactext_binary_present_value_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_reliability_name(
|
||||
unsigned index);
|
||||
const char *bactext_device_status_name(
|
||||
unsigned index);
|
||||
const char *bactext_segmentation_name(
|
||||
unsigned index);
|
||||
bool bactext_segmentation_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
const char *bactext_node_type_name(
|
||||
unsigned index);
|
||||
const char *bactext_character_string_encoding_name(
|
||||
unsigned index);
|
||||
const char *bactext_event_transition_name(
|
||||
unsigned index);
|
||||
bool bactext_event_transition_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
|
||||
const char *bactext_days_of_week_name(
|
||||
unsigned index);
|
||||
bool bactext_days_of_week_index(
|
||||
const char *search_name,
|
||||
unsigned *found_index);
|
||||
|
||||
const char *bactext_network_layer_msg_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_life_safety_state_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_device_communications_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_operation_name(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_in_progress(
|
||||
unsigned index);
|
||||
|
||||
const char *bactext_lighting_transition(
|
||||
unsigned index);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2015 Nikola Jelic
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bactimevalue.h"
|
||||
|
||||
int bacapp_encode_time_value(uint8_t *apdu, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_application_time(&apdu[apdu_len], &value->Time);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_application_data(&apdu[apdu_len], &value->Value);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int bacapp_encode_context_time_value(
|
||||
uint8_t *apdu, uint8_t tag_number, BACNET_TIME_VALUE *value)
|
||||
{
|
||||
int len;
|
||||
int apdu_len = 0;
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_encode_time_value(&apdu[apdu_len], value);
|
||||
apdu_len += len;
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], tag_number);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
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) {
|
||||
return -1;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
len = bacapp_decode_application_data(&apdu[apdu_len], 2048, &value->Value);
|
||||
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 len = 0;
|
||||
int section_length;
|
||||
|
||||
if (decode_is_opening_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
section_length = bacapp_decode_time_value(&apdu[len], value);
|
||||
if (section_length > 0) {
|
||||
len += section_length;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_closing_tag_number(&apdu[len], tag_number)) {
|
||||
len++;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef _BAC_TIME_VALUE_H_
|
||||
#define _BAC_TIME_VALUE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
|
||||
typedef struct {
|
||||
BACNET_TIME Time;
|
||||
BACNET_APPLICATION_DATA_VALUE Value;
|
||||
} BACNET_TIME_VALUE;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
int bacapp_encode_time_value(uint8_t * apdu,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_encode_context_time_value(uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_decode_time_value(uint8_t * apdu,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
int bacapp_decode_context_time_value(uint8_t * apdu,
|
||||
uint8_t tag_number,
|
||||
BACNET_TIME_VALUE * value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic BBMD for BVLC IPv6 handler
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef BVLC6_HANDLER_H
|
||||
#define BVLC6_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/datalink/bvlc6.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* user application function prototypes */
|
||||
int bvlc6_handler(
|
||||
BACNET_IP6_ADDRESS *addr,
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * npdu,
|
||||
uint16_t npdu_len);
|
||||
int bvlc6_register_with_bbmd(
|
||||
BACNET_IP6_ADDRESS *bbmd_addr,
|
||||
uint32_t vmac_src,
|
||||
uint16_t time_to_live_seconds);
|
||||
uint16_t bvlc6_get_last_result(
|
||||
void);
|
||||
uint8_t bvlc6_get_function_code(
|
||||
void);
|
||||
void bvlc6_init(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,320 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2015 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/basic/sys/keylist.h"
|
||||
/* me! */
|
||||
#include "bacnet/basic/bbmd6/vmac.h"
|
||||
|
||||
/** @file
|
||||
Handle VMAC address binding */
|
||||
|
||||
/* This module is used to handle the virtual MAC address binding that */
|
||||
/* occurs in BACnet for ZigBee or IPv6. */
|
||||
|
||||
/* Key List for storing the object data sorted by instance number */
|
||||
static OS_Keylist VMAC_List;
|
||||
|
||||
/**
|
||||
* Returns the number of VMAC in the list
|
||||
*/
|
||||
unsigned int VMAC_Count(void)
|
||||
{
|
||||
return (unsigned int)Keylist_Count(VMAC_List);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a VMAC to the list
|
||||
*
|
||||
* @param device_id - BACnet device object instance number
|
||||
* @param src - BACnet/IPv6 address
|
||||
*
|
||||
* @return true if the device ID and MAC are added
|
||||
*/
|
||||
bool VMAC_Add(uint32_t device_id, struct vmac_data *src)
|
||||
{
|
||||
bool status = false;
|
||||
struct vmac_data *pVMAC = NULL;
|
||||
int index = 0;
|
||||
size_t i = 0;
|
||||
|
||||
pVMAC = Keylist_Data(VMAC_List, device_id);
|
||||
if (!pVMAC) {
|
||||
pVMAC = calloc(1, sizeof(struct vmac_data));
|
||||
if (pVMAC) {
|
||||
/* copy the MAC into the data store */
|
||||
for (i = 0; i < sizeof(pVMAC->mac); i++) {
|
||||
if (i < src->mac_len) {
|
||||
pVMAC->mac[i] = src->mac[i];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
pVMAC->mac_len = src->mac_len;
|
||||
index = Keylist_Data_Add(VMAC_List, device_id, pVMAC);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
printf("VMAC %u added.\n", (unsigned int)device_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a VMAC in the list by seeking the Device ID, and deletes it.
|
||||
*
|
||||
* @param device_id - BACnet device object instance number
|
||||
*
|
||||
* @return pointer to the VMAC data from the list - be sure to free() it!
|
||||
*/
|
||||
bool VMAC_Delete(uint32_t device_id)
|
||||
{
|
||||
bool status = false;
|
||||
struct vmac_data *pVMAC;
|
||||
|
||||
pVMAC = Keylist_Data_Delete(VMAC_List, device_id);
|
||||
if (pVMAC) {
|
||||
free(pVMAC);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a VMAC in the list by seeking the Device ID.
|
||||
*
|
||||
* @param device_id - BACnet device object instance number
|
||||
*
|
||||
* @return pointer to the VMAC data from the list
|
||||
*/
|
||||
struct vmac_data *VMAC_Find_By_Key(uint32_t device_id)
|
||||
{
|
||||
return Keylist_Data(VMAC_List, device_id);
|
||||
}
|
||||
|
||||
/** Compare the VMAC address
|
||||
*
|
||||
* @param vmac1 - VMAC address that will be compared to vmac2
|
||||
* @param vmac2 - VMAC address that will be compared to vmac1
|
||||
*
|
||||
* @return true if the addresses are different
|
||||
*/
|
||||
bool VMAC_Different(struct vmac_data *vmac1, struct vmac_data *vmac2)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned int i = 0;
|
||||
unsigned int mac_len = VMAC_MAC_MAX;
|
||||
|
||||
if (vmac1 && vmac2) {
|
||||
if (vmac1->mac_len != vmac2->mac_len) {
|
||||
status = true;
|
||||
} else {
|
||||
if (vmac1->mac_len < mac_len) {
|
||||
mac_len = (unsigned int)vmac1->mac_len;
|
||||
}
|
||||
for (i = 0; i < mac_len; i++) {
|
||||
if (vmac1->mac[i] != vmac2->mac[i]) {
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/** Compare the VMAC address
|
||||
*
|
||||
* @param vmac1 - VMAC address that will be compared to vmac2
|
||||
* @param vmac2 - VMAC address that will be compared to vmac1
|
||||
*
|
||||
* @return true if the addresses are the same
|
||||
*/
|
||||
bool VMAC_Match(struct vmac_data *vmac1, struct vmac_data *vmac2)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned int i = 0;
|
||||
unsigned int mac_len = VMAC_MAC_MAX;
|
||||
|
||||
if (vmac1 && vmac2 && vmac1->mac_len) {
|
||||
status = true;
|
||||
if (vmac1->mac_len != vmac2->mac_len) {
|
||||
status = false;
|
||||
} else {
|
||||
if (vmac1->mac_len < mac_len) {
|
||||
mac_len = (unsigned int)vmac1->mac_len;
|
||||
}
|
||||
for (i = 0; i < mac_len; i++) {
|
||||
if (vmac1->mac[i] != vmac2->mac[i]) {
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a VMAC in the list by seeking a matching VMAC address
|
||||
*
|
||||
* @param vmac - VMAC address that will be sought
|
||||
* @param device_id - BACnet device object instance number
|
||||
*
|
||||
* @return true if the VMAC address was found
|
||||
*/
|
||||
bool VMAC_Find_By_Data(struct vmac_data *vmac, uint32_t *device_id)
|
||||
{
|
||||
bool status = false;
|
||||
struct vmac_data *list_vmac;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
|
||||
count = Keylist_Count(VMAC_List);
|
||||
while (count) {
|
||||
index = count - 1;
|
||||
list_vmac = Keylist_Data_Index(VMAC_List, index);
|
||||
if (list_vmac) {
|
||||
if (VMAC_Match(vmac, list_vmac)) {
|
||||
if (device_id) {
|
||||
*device_id = Keylist_Key(VMAC_List, index);
|
||||
}
|
||||
status = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
count--;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the memory used by the VMAC list data
|
||||
*/
|
||||
void VMAC_Cleanup(void)
|
||||
{
|
||||
struct vmac_data *pVMAC;
|
||||
|
||||
if (VMAC_List) {
|
||||
do {
|
||||
pVMAC = Keylist_Data_Pop(VMAC_List);
|
||||
if (pVMAC) {
|
||||
free(pVMAC);
|
||||
}
|
||||
} while (pVMAC);
|
||||
Keylist_Delete(VMAC_List);
|
||||
VMAC_List = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the VMAC list data
|
||||
*/
|
||||
void VMAC_Init(void)
|
||||
{
|
||||
VMAC_List = Keylist_Create();
|
||||
if (VMAC_List) {
|
||||
atexit(VMAC_Cleanup);
|
||||
printf("VMAC List initialized.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
void testVMAC(Test *pTest)
|
||||
{
|
||||
uint32_t device_id = 123;
|
||||
uint32_t test_device_id = 0;
|
||||
struct vmac_data test_vmac_data;
|
||||
struct vmac_data *pVMAC;
|
||||
unsigned int i = 0;
|
||||
bool status = false;
|
||||
|
||||
VMAC_Init();
|
||||
for (i = 0; i < VMAC_MAC_MAX; i++) {
|
||||
test_vmac_data.mac[i] = 1 + i;
|
||||
}
|
||||
test_vmac_data.mac_len = VMAC_MAC_MAX;
|
||||
status = VMAC_Add(device_id, &test_vmac_data);
|
||||
ct_test(pTest, status);
|
||||
pVMAC = VMAC_Find_By_Key(0);
|
||||
ct_test(pTest, pVMAC == NULL);
|
||||
pVMAC = VMAC_Find_By_Key(device_id);
|
||||
ct_test(pTest, pVMAC);
|
||||
status = VMAC_Different(pVMAC, &test_vmac_data);
|
||||
ct_test(pTest, !status);
|
||||
status = VMAC_Match(pVMAC, &test_vmac_data);
|
||||
ct_test(pTest, status);
|
||||
status = VMAC_Find_By_Data(&test_vmac_data, &test_device_id);
|
||||
ct_test(pTest, status);
|
||||
ct_test(pTest, test_device_id == device_id);
|
||||
status = VMAC_Delete(device_id);
|
||||
ct_test(pTest, status);
|
||||
pVMAC = VMAC_Find_By_Key(device_id);
|
||||
ct_test(pTest, pVMAC == NULL);
|
||||
VMAC_Cleanup();
|
||||
}
|
||||
|
||||
#ifdef TEST_VMAC
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet VMAC", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testVMAC);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,127 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2012 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef ADDRESS_H
|
||||
#define ADDRESS_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/readrange.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void address_init(
|
||||
void);
|
||||
|
||||
void address_init_partial(
|
||||
void);
|
||||
|
||||
void address_add(
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
void address_remove_device(
|
||||
uint32_t device_id);
|
||||
|
||||
bool address_get_by_device(
|
||||
uint32_t device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_get_by_index(
|
||||
unsigned index,
|
||||
uint32_t * device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_device_get_by_index(
|
||||
unsigned index,
|
||||
uint32_t * device_id,
|
||||
uint32_t * device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_get_device_id(
|
||||
BACNET_ADDRESS * src,
|
||||
uint32_t * device_id);
|
||||
|
||||
unsigned address_count(
|
||||
void);
|
||||
|
||||
bool address_match(
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_bind_request(
|
||||
uint32_t device_id,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
bool address_device_bind_request(
|
||||
uint32_t device_id,
|
||||
uint32_t * device_ttl,
|
||||
unsigned *max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
void address_add_binding(
|
||||
uint32_t device_id,
|
||||
unsigned max_apdu,
|
||||
BACNET_ADDRESS * src);
|
||||
|
||||
int address_list_encode(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len);
|
||||
|
||||
int rr_address_list_encode(
|
||||
uint8_t * apdu,
|
||||
BACNET_READ_RANGE_DATA * pRequest);
|
||||
|
||||
void address_set_device_TTL(
|
||||
uint32_t device_id,
|
||||
uint32_t TimeOut,
|
||||
bool StaticFlag);
|
||||
|
||||
void address_cache_timer(
|
||||
uint16_t uSeconds);
|
||||
|
||||
void address_mac_init(
|
||||
BACNET_MAC_ADDRESS *mac,
|
||||
uint8_t *adr,
|
||||
uint8_t len);
|
||||
|
||||
bool address_mac_from_ascii(
|
||||
BACNET_MAC_ADDRESS *mac,
|
||||
char *arg);
|
||||
|
||||
void address_protected_entry_index_set(uint32_t top_protected_entry_index);
|
||||
void address_own_device_id_set(uint32_t own_id);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2008 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/** @file h_npdu.c Handles messages at the NPDU level of the BACnet stack. */
|
||||
|
||||
/** Handler for the NPDU portion of a received packet.
|
||||
* Aside from error-checking, if the NPDU doesn't contain routing info,
|
||||
* this handler doesn't do much besides stepping over the NPDU header
|
||||
* and passing the remaining bytes to the apdu_handler.
|
||||
* @note The routing (except src) and NCPI information, including
|
||||
* npdu_data->data_expecting_reply, are discarded.
|
||||
* @see routing_npdu_handler
|
||||
*
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void npdu_handler(BACNET_ADDRESS *src, /* source address */
|
||||
uint8_t *pdu, /* PDU data */
|
||||
uint16_t pdu_len)
|
||||
{ /* length PDU */
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (npdu_data.network_layer_message) {
|
||||
/*FIXME: network layer message received! Handle it! */
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "NPDU: Network Layer Message discarded!\n");
|
||||
#endif
|
||||
} else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) {
|
||||
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
|
||||
/* only handle the version that we know how to handle */
|
||||
/* and we are not a router, so ignore messages with
|
||||
routing information cause they are not for us */
|
||||
if ((dest.net == BACNET_BROADCAST_NETWORK) &&
|
||||
((pdu[apdu_offset] & 0xF0) ==
|
||||
PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
|
||||
/* hack for 5.4.5.1 - IDLE */
|
||||
/* ConfirmedBroadcastReceived */
|
||||
/* then enter IDLE - ignore the PDU */
|
||||
} else {
|
||||
apdu_handler(src, &pdu[apdu_offset],
|
||||
(uint16_t)(pdu_len - apdu_offset));
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: DNET=%u. Discarded!\n", (unsigned)dest.net);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if PRINT_ENABLED
|
||||
printf("NPDU: BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned)pdu[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic NPDU handler
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef NPDU_HANDLER_H
|
||||
#define NPDU_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void npdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
void npdu_handler_cleanup(void);
|
||||
void npdu_handler_init(
|
||||
uint16_t bip_net,
|
||||
uint16_t mstp_net);
|
||||
void npdu_router_handler(
|
||||
uint16_t snet,
|
||||
BACNET_ADDRESS * src,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
int npdu_router_send_pdu(
|
||||
uint16_t dnet,
|
||||
BACNET_ADDRESS * dest,
|
||||
BACNET_NPDU_DATA * npdu_data,
|
||||
uint8_t * pdu,
|
||||
unsigned int pdu_len);
|
||||
void npdu_router_get_my_address(
|
||||
uint16_t dnet,
|
||||
BACNET_ADDRESS * my_address);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,302 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2010 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
/* Acknowledging the contribution of code and ideas used here that
|
||||
* came from Paul Chapman's vmac demo project. */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacint.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bits.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#if PRINT_ENABLED
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#if defined(BACDL_BIP)
|
||||
#include "bacnet/datalink/bvlc.h"
|
||||
#endif
|
||||
|
||||
/** @file h_routed_npdu.c Handles messages at the NPDU level of the BACnet
|
||||
* stack, including routing and network control messages. */
|
||||
|
||||
/** Handler to manage the Network Layer Control Messages received in a packet.
|
||||
* This handler is called if the NCPI bit 7 indicates that this packet is a
|
||||
* network layer message and there is no further DNET to pass it to.
|
||||
* The NCPI has already been decoded into the npdu_data structure.
|
||||
* @ingroup MISCHNDLR
|
||||
*
|
||||
* @param src [in] The routing source information, if any.
|
||||
* If src->net and src->len are 0, there is no
|
||||
* routing source information.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param npdu_data [in] Contains a filled-out structure with information
|
||||
* decoded from the NCPI and other NPDU
|
||||
* bytes.
|
||||
* @param npdu [in] Buffer containing the rest of the NPDU, following the
|
||||
* bytes that have already been decoded.
|
||||
* @param npdu_len [in] The length of the remaining NPDU message in npdu[].
|
||||
*/
|
||||
static void network_control_handler(BACNET_ADDRESS *src,
|
||||
int *DNET_list,
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
uint8_t *npdu,
|
||||
uint16_t npdu_len)
|
||||
{
|
||||
uint16_t npdu_offset = 0;
|
||||
uint16_t dnet = 0;
|
||||
uint16_t len = 0;
|
||||
|
||||
switch (npdu_data->network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
/* Send I-am-router-to-network with our one-network list if
|
||||
* our specific network is requested, or no specific
|
||||
* network is requested. Silently drop other DNET requests.
|
||||
*/
|
||||
if (npdu_len >= 2) {
|
||||
uint16_t network;
|
||||
len += decode_unsigned16(&npdu[len], &network);
|
||||
if (network == DNET_list[0]) {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
} else {
|
||||
Send_I_Am_Router_To_Network(DNET_list);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
/* Per the standard, we are supposed to process this message and
|
||||
* add its DNETs to our routing table.
|
||||
* However, since we only have one upstream port that these
|
||||
* messages can come from and replies go to, it doesn't seem
|
||||
* to provide us any value to do this; when we need to send to
|
||||
* some remote device, we will start by pushing it out the
|
||||
* upstream port and let the attached router(s) take it from there.
|
||||
* Consequently, we'll do nothing interesting here.
|
||||
* -- Unless we act upon NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK
|
||||
* later for congestion control - then it could matter.
|
||||
*/
|
||||
debug_printf("%s for Networks: ",
|
||||
bactext_network_layer_msg_name(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK));
|
||||
while (npdu_len >= 2) {
|
||||
len = decode_unsigned16(&npdu[npdu_offset], &dnet);
|
||||
debug_printf("%hu", dnet);
|
||||
npdu_len -= len;
|
||||
npdu_offset += len;
|
||||
if (npdu_len >= 2) {
|
||||
debug_printf(", ");
|
||||
}
|
||||
}
|
||||
debug_printf("\n");
|
||||
break;
|
||||
case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
|
||||
/* Do nothing, same as previous case. */
|
||||
break;
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
if (npdu_len >= 3) {
|
||||
decode_unsigned16(&npdu[1], &dnet);
|
||||
debug_printf("Received %s for Network: ",
|
||||
bactext_network_layer_msg_name(
|
||||
NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK));
|
||||
debug_printf("%hu, Reason code: %d \n", dnet, npdu[0]);
|
||||
}
|
||||
break;
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
/* Do nothing - don't support upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
/* If sent with Number of Ports == 0, we respond with
|
||||
* NETWORK_MESSAGE_INIT_RT_TABLE_ACK and a list of all our
|
||||
* reachable networks.
|
||||
*/
|
||||
if (npdu_len > 0) {
|
||||
/* If Number of Ports is 0, broadcast our "full" table */
|
||||
if (npdu[0] == 0) {
|
||||
Send_Initialize_Routing_Table_Ack(NULL, DNET_list);
|
||||
} else {
|
||||
/* If they sent us a list, just politely ACK it
|
||||
* with no routing list of our own. But we don't DO
|
||||
* anything with the info, either.
|
||||
*/
|
||||
int listTerminator = -1;
|
||||
Send_Initialize_Routing_Table_Ack(src, &listTerminator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Else, fall through to do nothing. */
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* Do nothing with the routing table info, since don't support
|
||||
* upstream traffic congestion control */
|
||||
break;
|
||||
case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
|
||||
/* Do nothing - don't support PTP half-router control */
|
||||
break;
|
||||
default:
|
||||
/* An unrecognized message is bad; send an error response. */
|
||||
Send_Reject_Message_To_Network(
|
||||
src, NETWORK_REJECT_UNKNOWN_MESSAGE_TYPE, DNET_list[0]);
|
||||
/* Sending our DNET doesn't make a lot of sense, does it? */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** An APDU pre-handler that makes sure that the subsequent APDU handler call
|
||||
* operates on the right Device Object(s), as addressed by the destination
|
||||
* (routing) information.
|
||||
*
|
||||
* @note Even when the destination is "routed" to our virtual BACnet network,
|
||||
* the src information does not need to change to reflect that (as it normally
|
||||
* would for a routed message) because the reply will be sent from the level
|
||||
* of the gateway Device.
|
||||
*
|
||||
* @param src [in] The BACNET_ADDRESS of the message's source.
|
||||
* @param dest [in] The BACNET_ADDRESS of the message's destination.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param apdu [in] The apdu portion of the request, to be processed.
|
||||
* @param apdu_len [in] The total (remaining) length of the apdu.
|
||||
*/
|
||||
static void routed_apdu_handler(BACNET_ADDRESS *src,
|
||||
BACNET_ADDRESS *dest,
|
||||
int *DNET_list,
|
||||
uint8_t *apdu,
|
||||
uint16_t apdu_len)
|
||||
{
|
||||
int cursor = 0; /* Starting hint */
|
||||
bool bGotOne = false;
|
||||
|
||||
if (!Routed_Device_Is_Valid_Network(dest->net, DNET_list)) {
|
||||
/* We don't know how to reach this one.
|
||||
* The protocol doesn't specifically state this, but if this message
|
||||
* was broadcast to us, we should assume "someone else" is handling
|
||||
* it and not get involved (ie, send a Reject-Message).
|
||||
* Since we can't reach other routers that src couldn't already reach,
|
||||
* we don't try the standard path of asking Who-Is-Router-to-Network. */
|
||||
#if defined(BACDL_BIP)
|
||||
/* If wasn't unicast to us, must have been one of the bcast types.
|
||||
* Drop it. */
|
||||
if (bvlc_get_function_code() != BVLC_ORIGINAL_UNICAST_NPDU) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/* Upper level handlers knew that this was sent as a bcast,
|
||||
* but our only other way to guess at that here is if the dest->adr
|
||||
* is absent, then we know this is some sort of bcast.
|
||||
*/
|
||||
if (dest->len > 0) {
|
||||
Send_Reject_Message_To_Network(
|
||||
src, NETWORK_REJECT_NO_ROUTE, dest->net);
|
||||
} /* else, silently drop it */
|
||||
return;
|
||||
}
|
||||
|
||||
while (Routed_Device_GetNext(dest, DNET_list, &cursor)) {
|
||||
apdu_handler(src, apdu, apdu_len);
|
||||
bGotOne = true;
|
||||
if (cursor < 0) { /* If no more matches, */
|
||||
break; /* We don't need to keep looking */
|
||||
}
|
||||
}
|
||||
if (!bGotOne) {
|
||||
/* Just silently drop this packet. */
|
||||
}
|
||||
}
|
||||
|
||||
/** Handler for the NPDU portion of a received packet, which may have routing.
|
||||
* This is a fuller handler than the regular npdu_handler, as it manages
|
||||
* - Decoding of the NCPI byte
|
||||
* - Further processing by network_control_handler() if this is a network
|
||||
* layer message.
|
||||
* - Further processing by routed_apdu_handler() if it contains an APDU
|
||||
* - Normally (no routing) by apdu_handler()
|
||||
* - With Routing (a further destination was indicated) by the decoded
|
||||
* destination.
|
||||
* - Errors in decoding.
|
||||
* @note The npdu_data->data_expecting_reply status is discarded.
|
||||
* @see npdu_handler
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param src [out] Returned with routing source information if the NPDU
|
||||
* has any and if this points to non-null storage for it.
|
||||
* If src->net and src->len are 0 on return, there is no
|
||||
* routing source information.
|
||||
* This src describes the original source of the message when
|
||||
* it had to be routed to reach this BACnet Device, and this
|
||||
* is passed down into the apdu_handler; however, I don't
|
||||
* think this project's code has any use for the src info
|
||||
* on return from this handler, since the response has
|
||||
* already been sent via the apdu_handler.
|
||||
* @param DNET_list [in] List of our reachable downstream BACnet Network
|
||||
* numbers. Normally just one valid entry; terminated with a -1 value.
|
||||
* @param pdu [in] Buffer containing the NPDU and APDU of the received packet.
|
||||
* @param pdu_len [in] The size of the received message in the pdu[] buffer.
|
||||
*/
|
||||
void routing_npdu_handler(
|
||||
BACNET_ADDRESS *src, int *DNET_list, uint8_t *pdu, uint16_t pdu_len)
|
||||
{
|
||||
int apdu_offset = 0;
|
||||
BACNET_ADDRESS dest = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
|
||||
/* only handle the version that we know how to handle */
|
||||
if (pdu[0] == BACNET_PROTOCOL_VERSION) {
|
||||
apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data);
|
||||
if (apdu_offset <= 0) {
|
||||
debug_printf("NPDU: Decoding failed; Discarded!\n");
|
||||
} else if (npdu_data.network_layer_message) {
|
||||
if ((dest.net == 0) || (dest.net == BACNET_BROADCAST_NETWORK)) {
|
||||
network_control_handler(src, DNET_list, &npdu_data,
|
||||
&pdu[apdu_offset], (uint16_t)(pdu_len - apdu_offset));
|
||||
} else {
|
||||
/* The DNET is set, but we don't support downstream routers,
|
||||
* so we just silently drop this network layer message,
|
||||
* since only routers can handle it (even if for our DNET) */
|
||||
}
|
||||
} else if (apdu_offset <= pdu_len) {
|
||||
if ((dest.net == 0) || (npdu_data.hop_count > 1)) {
|
||||
routed_apdu_handler(src, &dest, DNET_list, &pdu[apdu_offset],
|
||||
(uint16_t)(pdu_len - apdu_offset));
|
||||
}
|
||||
/* Else, hop_count bottomed out and we discard this one. */
|
||||
}
|
||||
} else {
|
||||
/* Should we send NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK? */
|
||||
debug_printf(
|
||||
"NPDU: Unsupported BACnet Protocol Version=%u. Discarded!\n",
|
||||
(unsigned)pdu[0]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic routing NPDU handler
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef ROUTING_NPDU_HANDLER_H
|
||||
#define ROUTING_NPDU_HANDLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void routing_npdu_handler(
|
||||
BACNET_ADDRESS * src,
|
||||
int *DNET_list,
|
||||
uint8_t * pdu,
|
||||
uint16_t pdu_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,320 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/bactext.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
/** @file s_router.c Methods to send various BACnet Router Network Layer
|
||||
* Messages. */
|
||||
|
||||
/** Initialize an npdu_data structure with given parameters and good defaults,
|
||||
* and add the Network Layer Message fields.
|
||||
* The name is a misnomer, as it doesn't do any actual encoding here.
|
||||
* @see npdu_encode_npdu_data for a simpler version to use when sending an
|
||||
* APDU instead of a Network Layer Message.
|
||||
*
|
||||
* @param npdu_data [out] Returns a filled-out structure with information
|
||||
* provided by the other arguments and
|
||||
* good defaults.
|
||||
* @param network_message_type [in] The type of Network Layer Message.
|
||||
* @param data_expecting_reply [in] True if message should have a reply.
|
||||
* @param priority [in] One of the 4 priorities defined in section 6.2.2,
|
||||
* like B'11' = Life Safety message
|
||||
*/
|
||||
void npdu_encode_npdu_network(BACNET_NPDU_DATA *npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply,
|
||||
BACNET_MESSAGE_PRIORITY priority)
|
||||
{
|
||||
if (npdu_data) {
|
||||
npdu_data->data_expecting_reply = data_expecting_reply;
|
||||
npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
|
||||
npdu_data->network_layer_message = true; /* false if APDU */
|
||||
npdu_data->network_message_type = network_message_type; /* optional */
|
||||
npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */
|
||||
npdu_data->priority = priority;
|
||||
npdu_data->hop_count = HOP_COUNT_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/** Function to encode and send any supported Network Layer Message.
|
||||
* The payload for the message is encoded from information in the iArgs[] array.
|
||||
* The contents of iArgs are are, per message type:
|
||||
* - NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK: Single int for DNET requested
|
||||
* - NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK: Array of DNET(s) to send,
|
||||
* terminated with -1
|
||||
* - NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK: array of 2 ints,
|
||||
* first is reason, second is DNET of interest
|
||||
* - NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK: same as I-Am-Router msg
|
||||
* - NETWORK_MESSAGE_INIT_RT_TABLE and NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
* Array of DNET(s) to process as "Ports", terminated with -1. Each DNET
|
||||
* will be expanded to a BACNET_ROUTER_PORT (with simple defaults for
|
||||
* most fields) and encoded.
|
||||
*
|
||||
* @param network_message_type [in] The type of message to be sent.
|
||||
* @param dst [in/out] If not NULL, contains the destination for the message.
|
||||
* @param iArgs [in] An optional array of values whose meaning depends on
|
||||
* the type of message.
|
||||
* @return Number of bytes sent, or <=0 if no message was sent.
|
||||
*/
|
||||
int Send_Network_Layer_Message(BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
BACNET_ADDRESS *dst,
|
||||
int *iArgs)
|
||||
{
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
int *pVal = iArgs; /* Start with first value */
|
||||
bool data_expecting_reply = false;
|
||||
BACNET_NPDU_DATA npdu_data;
|
||||
BACNET_ADDRESS bcastDest;
|
||||
|
||||
if (iArgs == NULL) {
|
||||
return 0; /* Can't do anything here */
|
||||
}
|
||||
|
||||
/* If dst was NULL, get our (local net) broadcast MAC address. */
|
||||
if (dst == NULL) {
|
||||
datalink_get_broadcast_address(&bcastDest);
|
||||
dst = &bcastDest;
|
||||
}
|
||||
|
||||
if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE) {
|
||||
data_expecting_reply = true; /* DER in this one case */
|
||||
}
|
||||
npdu_encode_npdu_network(&npdu_data, network_message_type,
|
||||
data_expecting_reply, MESSAGE_PRIORITY_NORMAL);
|
||||
|
||||
/* We don't need src information, since a message can't originate from
|
||||
* our downstream BACnet network.
|
||||
*/
|
||||
pdu_len =
|
||||
npdu_encode_pdu(&Handler_Transmit_Buffer[0], dst, NULL, &npdu_data);
|
||||
|
||||
/* Now encode the optional payload bytes, per message type */
|
||||
switch (network_message_type) {
|
||||
case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
|
||||
if (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
}
|
||||
/* else, don't encode a DNET */
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
|
||||
case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
|
||||
while (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
pVal++;
|
||||
}
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
|
||||
/* Encode the Reason byte, then the DNET */
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t)*pVal;
|
||||
pVal++;
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
break;
|
||||
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE:
|
||||
case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
|
||||
/* First, count the number of Ports we will encode */
|
||||
len = 0; /* Re-purpose len as our counter here */
|
||||
while (*pVal >= 0) {
|
||||
len++;
|
||||
pVal++;
|
||||
}
|
||||
Handler_Transmit_Buffer[pdu_len++] = (uint8_t)len;
|
||||
|
||||
if (len > 0) {
|
||||
uint8_t portID = 1;
|
||||
pVal = iArgs; /* Reset to beginning */
|
||||
/* Now encode each (virtual) BACNET_ROUTER_PORT.
|
||||
* We will simply use a positive index for PortID,
|
||||
* and have no PortInfo.
|
||||
*/
|
||||
while (*pVal >= 0) {
|
||||
len = encode_unsigned16(
|
||||
&Handler_Transmit_Buffer[pdu_len], (uint16_t)*pVal);
|
||||
pdu_len += len;
|
||||
Handler_Transmit_Buffer[pdu_len++] = portID++;
|
||||
Handler_Transmit_Buffer[pdu_len++] = 0;
|
||||
debug_printf(
|
||||
" Sending Routing Table entry for %u \n", *pVal);
|
||||
pVal++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
debug_printf("Not sent: %s message unsupported \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
return 0;
|
||||
break; /* Will never reach this line */
|
||||
}
|
||||
|
||||
if (dst != NULL) {
|
||||
debug_printf("Sending %s message to BACnet network %u \n",
|
||||
bactext_network_layer_msg_name(network_message_type), dst->net);
|
||||
} else {
|
||||
debug_printf("Sending %s message to local BACnet network \n",
|
||||
bactext_network_layer_msg_name(network_message_type));
|
||||
}
|
||||
|
||||
/* Now send the message */
|
||||
bytes_sent = datalink_send_pdu(
|
||||
dst, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
#if PRINT_ENABLED
|
||||
if (bytes_sent <= 0) {
|
||||
int wasErrno = errno; /* preserve the errno */
|
||||
debug_printf("Failed to send %s message (%s)!\n",
|
||||
bactext_network_layer_msg_name(network_message_type),
|
||||
strerror(wasErrno));
|
||||
}
|
||||
#endif
|
||||
return bytes_sent;
|
||||
}
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Optionally may designate a particular router
|
||||
* destination to respond.
|
||||
* @param dnet [in] Which BACnet network to request for; if -1, no DNET
|
||||
* will be sent and the receiving router(s) will send
|
||||
* their full list of reachable BACnet networks.
|
||||
*/
|
||||
void Send_Who_Is_Router_To_Network(BACNET_ADDRESS *dst, int dnet)
|
||||
{
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, dst, &dnet);
|
||||
}
|
||||
|
||||
/** Broadcast an I-am-router-to-network message, giving the list of networks
|
||||
* we can reach.
|
||||
* The message will be sent to our normal DataLink Layer interface,
|
||||
* not the routed backend.
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1
|
||||
*/
|
||||
void Send_I_Am_Router_To_Network(const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, NULL, (int *)DNET_list);
|
||||
}
|
||||
|
||||
/** Finds a specific router, or all reachable BACnet networks.
|
||||
* The response(s) will come in I-am-router-to-network message(s).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, request will be broadcast to the local BACnet
|
||||
* network. Otherwise, designates a particular router
|
||||
* destination.
|
||||
* @param reject_reason [in] One of the BACNET_NETWORK_REJECT_REASONS codes.
|
||||
* @param dnet [in] Which BACnet network orginated the message.
|
||||
*/
|
||||
void Send_Reject_Message_To_Network(
|
||||
BACNET_ADDRESS *dst, uint8_t reject_reason, int dnet)
|
||||
{
|
||||
int iArgs[2];
|
||||
iArgs[0] = reject_reason;
|
||||
iArgs[1] = dnet;
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK, dst, iArgs);
|
||||
debug_printf(" Reject Reason=%d, DNET=%u\n", reject_reason, dnet);
|
||||
}
|
||||
|
||||
/** Send an Initialize Routing Table message, built from an optional DNET[]
|
||||
* array.
|
||||
* There are two cases here:
|
||||
* 1) We are requesting a destination router's Routing Table.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* 2) We are sending out our Routing Table for some reason (normally bcast it).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, msg will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when requesting a Routing Table.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. Will be just -1 when we are
|
||||
* requesting a routing table.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table(BACNET_ADDRESS *dst, const int DNET_list[])
|
||||
{
|
||||
/* Use a NULL dst here since we want a broadcast MAC address. */
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE, dst, (int *)DNET_list);
|
||||
}
|
||||
|
||||
/** Sends our Routing Table, built from our DNET[] array, as an ACK.
|
||||
* There are two cases here:
|
||||
* 1) We are responding to a NETWORK_MESSAGE_INIT_RT_TABLE requesting our table.
|
||||
* We will normally broadcast that response.
|
||||
* 2) We are ACKing the receipt of a NETWORK_MESSAGE_INIT_RT_TABLE containing a
|
||||
* routing table, and then we will want to respond to that dst router.
|
||||
* In that case, DNET[] should just have one entry of -1 (no routing table
|
||||
* is sent).
|
||||
* @ingroup NMRC
|
||||
*
|
||||
* @param dst [in] If NULL, Ack will be broadcast to the local BACnet network.
|
||||
* Optionally may designate a particular router destination,
|
||||
* especially when ACKing receipt of this message type.
|
||||
* @param DNET_list [in] List of BACnet network numbers for which I am a router,
|
||||
* terminated with -1. May be just -1 when no table
|
||||
* should be sent.
|
||||
*/
|
||||
void Send_Initialize_Routing_Table_Ack(
|
||||
BACNET_ADDRESS *dst, const int DNET_list[])
|
||||
{
|
||||
Send_Network_Layer_Message(
|
||||
NETWORK_MESSAGE_INIT_RT_TABLE_ACK, dst, (int *)DNET_list);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date October 2019
|
||||
* @brief Header file for a basic WritePropertyMultiple service send
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef SEND_ROUTER_H
|
||||
#define SEND_ROUTER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void npdu_encode_npdu_network(
|
||||
BACNET_NPDU_DATA *npdu_data,
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
bool data_expecting_reply,
|
||||
BACNET_MESSAGE_PRIORITY priority);
|
||||
int Send_Network_Layer_Message(
|
||||
BACNET_NETWORK_MESSAGE_TYPE network_message_type,
|
||||
BACNET_ADDRESS * dst,
|
||||
int *iArgs);
|
||||
void Send_Who_Is_Router_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
int dnet);
|
||||
void Send_I_Am_Router_To_Network(
|
||||
const int DNET_list[]);
|
||||
void Send_Reject_Message_To_Network(
|
||||
BACNET_ADDRESS * dst,
|
||||
uint8_t reject_reason,
|
||||
int dnet);
|
||||
void Send_Initialize_Routing_Table(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[]);
|
||||
void Send_Initialize_Routing_Table_Ack(
|
||||
BACNET_ADDRESS * dst,
|
||||
const int DNET_list[]);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,146 @@
|
||||
# Unit tests for the BACnet Stack project
|
||||
|
||||
LOGFILE = test.log
|
||||
|
||||
all: ai ao av bi bo bv csv lc lo lsp \
|
||||
mso msv ms-input netport osv piv command \
|
||||
access_credential access_door access_point access_rights \
|
||||
access_user access_zone credential_data_input
|
||||
|
||||
clean: logfile
|
||||
rm ${LOGFILE}
|
||||
|
||||
logfile:
|
||||
touch ${LOGFILE}
|
||||
|
||||
report:
|
||||
cat ${LOGFILE}
|
||||
|
||||
access_credential: logfile access_credential.mak
|
||||
$(MAKE) -s -f access_credential.mak clean all
|
||||
( ./access_credential >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_credential.mak clean
|
||||
|
||||
access_door: logfile access_door.mak
|
||||
$(MAKE) -s -f access_door.mak clean all
|
||||
( ./access_door >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_door.mak clean
|
||||
|
||||
access_point: logfile access_point.mak
|
||||
$(MAKE) -s -f access_point.mak clean all
|
||||
( ./access_point >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_point.mak clean
|
||||
|
||||
access_rights: logfile access_rights.mak
|
||||
$(MAKE) -s -f access_rights.mak clean all
|
||||
( ./access_rights >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_rights.mak clean
|
||||
|
||||
access_user: logfile access_user.mak
|
||||
$(MAKE) -s -f access_user.mak clean all
|
||||
( ./access_user >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_user.mak clean
|
||||
|
||||
access_zone: logfile access_zone.mak
|
||||
$(MAKE) -s -f access_zone.mak clean all
|
||||
( ./access_zone >> ${LOGFILE} )
|
||||
$(MAKE) -s -f access_zone.mak clean
|
||||
|
||||
credential_data_input: logfile credential_data_input.mak
|
||||
$(MAKE) -s -f credential_data_input.mak clean all
|
||||
( ./credential_data_input >> ${LOGFILE} )
|
||||
$(MAKE) -s -f credential_data_input.mak clean
|
||||
|
||||
ai: logfile ai.mak
|
||||
$(MAKE) -s -f ai.mak clean all
|
||||
( ./analog_input >> ${LOGFILE} )
|
||||
$(MAKE) -s -f ai.mak clean
|
||||
|
||||
ao: logfile ao.mak
|
||||
$(MAKE) -s -f ao.mak clean all
|
||||
( ./analog_output >> ${LOGFILE} )
|
||||
$(MAKE) -s -f ao.mak clean
|
||||
|
||||
av: logfile av.mak
|
||||
$(MAKE) -s -f av.mak clean all
|
||||
( ./analog_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f av.mak clean
|
||||
|
||||
bi: logfile bi.mak
|
||||
$(MAKE) -s -f bi.mak clean all
|
||||
$(MAKE) -s -f bi.mak clean
|
||||
|
||||
bo: logfile bo.mak
|
||||
$(MAKE) -s -f bo.mak clean all
|
||||
( ./binary_output >> ${LOGFILE} )
|
||||
$(MAKE) -s -f bo.mak clean
|
||||
|
||||
bv: logfile bv.mak
|
||||
$(MAKE) -s -f bv.mak clean all
|
||||
( ./binary_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f bv.mak clean
|
||||
|
||||
command: logfile command.mak
|
||||
$(MAKE) -s -f command.mak clean all
|
||||
( ./command >> ${LOGFILE} )
|
||||
$(MAKE) -s -f command.mak clean
|
||||
|
||||
csv: logfile csv.mak
|
||||
$(MAKE) -s -f csv.mak clean all
|
||||
( ./characterstring_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f csv.mak clean
|
||||
|
||||
device: logfile device.mak
|
||||
$(MAKE) -s -f device.mak clean all
|
||||
( ./device >> ${LOGFILE} )
|
||||
$(MAKE) -s -f device.mak clean
|
||||
|
||||
lc: logfile lc.mak
|
||||
$(MAKE) -s -f lc.mak clean all
|
||||
( ./load_control >> ${LOGFILE} )
|
||||
$(MAKE) -s -f lc.mak clean
|
||||
|
||||
lo: logfile lo.mak
|
||||
$(MAKE) -s -f lo.mak clean all
|
||||
( ./lighting_output >> ${LOGFILE} )
|
||||
$(MAKE) -s -f lo.mak clean
|
||||
|
||||
lsp: logfile lsp.mak
|
||||
$(MAKE) -s -f lsp.mak clean all
|
||||
( ./life_safety_point >> ${LOGFILE} )
|
||||
$(MAKE) -s -f lsp.mak clean
|
||||
|
||||
ms-input: logfile ms-input.mak
|
||||
$(MAKE) -s -f ms-input.mak clean all
|
||||
( ./multistate_input >> ${LOGFILE} )
|
||||
$(MAKE) -s -f ms-input.mak clean
|
||||
|
||||
mso: logfile mso.mak
|
||||
$(MAKE) -s -f mso.mak clean all
|
||||
( ./multistate_output >> ${LOGFILE} )
|
||||
$(MAKE) -s -f mso.mak clean
|
||||
|
||||
msv: logfile msv.mak
|
||||
$(MAKE) -s -f msv.mak clean all
|
||||
( ./multistate_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f msv.mak clean
|
||||
|
||||
osv: logfile osv.mak
|
||||
$(MAKE) -s -f osv.mak clean all
|
||||
( ./octetstring_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f osv.mak clean
|
||||
|
||||
netport: logfile netport.mak
|
||||
$(MAKE) -s -f netport.mak clean all
|
||||
( ./network_port >> ${LOGFILE} )
|
||||
$(MAKE) -s -f netport.mak clean
|
||||
|
||||
piv: logfile piv.mak
|
||||
$(MAKE) -s -f piv.mak clean all
|
||||
( ./positiveinteger_value >> ${LOGFILE} )
|
||||
$(MAKE) -s -f piv.mak clean
|
||||
|
||||
schedule: logfile schedule.mak
|
||||
$(MAKE) -s -f schedule.mak clean all
|
||||
( ./schedule >> ${LOGFILE} )
|
||||
$(MAKE) -s -f schedule.mak clean
|
||||
@@ -0,0 +1,442 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the credential to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access Credential Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/access_credential.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_Credential_Initialized = false;
|
||||
|
||||
static ACCESS_CREDENTIAL_DESCR ac_descr[MAX_ACCESS_CREDENTIALS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
|
||||
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_CREDENTIAL_STATUS,
|
||||
PROP_REASON_FOR_DISABLE, PROP_AUTHENTICATION_FACTORS, PROP_ACTIVATION_TIME,
|
||||
PROP_EXPIRATION_TIME, PROP_CREDENTIAL_DISABLE, PROP_ASSIGNED_ACCESS_RIGHTS,
|
||||
-1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_Credential_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_Credential_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Access_Credential_Initialized) {
|
||||
Access_Credential_Initialized = true;
|
||||
|
||||
for (i = 0; i < MAX_ACCESS_CREDENTIALS; i++) {
|
||||
ac_descr[i].global_identifier =
|
||||
0; /* set to some meaningful value */
|
||||
ac_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
ac_descr[i].credential_status = false;
|
||||
ac_descr[i].reasons_count = 0;
|
||||
ac_descr[i].auth_factors_count = 0;
|
||||
memset(&ac_descr[i].activation_time, 0, sizeof(BACNET_DATE_TIME));
|
||||
memset(&ac_descr[i].expiration_time, 0, sizeof(BACNET_DATE_TIME));
|
||||
ac_descr[i].credential_disable = ACCESS_CREDENTIAL_DISABLE_NONE;
|
||||
ac_descr[i].assigned_access_rights_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_Credential_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_CREDENTIALS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_Credential_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_CREDENTIALS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_Credential_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_Credential_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_CREDENTIALS;
|
||||
|
||||
if (object_instance < MAX_ACCESS_CREDENTIALS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_Credential_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_CREDENTIALS) {
|
||||
sprintf(text_string, "ACCESS CREDENTIAL %lu",
|
||||
(unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_Credential_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_Credential_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_CREDENTIAL, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_Credential_Object_Name(
|
||||
rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], OBJECT_ACCESS_CREDENTIAL);
|
||||
break;
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ac_descr[object_index].global_identifier);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ac_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_CREDENTIAL_STATUS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ac_descr[object_index].credential_status);
|
||||
break;
|
||||
case PROP_REASON_FOR_DISABLE:
|
||||
for (i = 0; i < ac_descr[object_index].reasons_count; i++) {
|
||||
len = encode_application_enumerated(
|
||||
&apdu[0], ac_descr[object_index].reason_for_disable[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_AUTHENTICATION_FACTORS:
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ac_descr[object_index].auth_factors_count);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0; i < ac_descr[object_index].auth_factors_count;
|
||||
i++) {
|
||||
len = bacapp_encode_credential_authentication_factor(
|
||||
&apdu[0], &ac_descr[object_index].auth_factors[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <=
|
||||
ac_descr[object_index].auth_factors_count) {
|
||||
apdu_len =
|
||||
bacapp_encode_credential_authentication_factor(&apdu[0],
|
||||
&ac_descr[object_index]
|
||||
.auth_factors[rpdata->array_index - 1]);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_ACTIVATION_TIME:
|
||||
apdu_len = bacapp_encode_datetime(
|
||||
&apdu[0], &ac_descr[object_index].activation_time);
|
||||
break;
|
||||
case PROP_EXPIRATION_TIME:
|
||||
apdu_len = bacapp_encode_datetime(
|
||||
&apdu[0], &ac_descr[object_index].expiration_time);
|
||||
break;
|
||||
case PROP_CREDENTIAL_DISABLE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ac_descr[object_index].credential_disable);
|
||||
break;
|
||||
case PROP_ASSIGNED_ACCESS_RIGHTS:
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len = encode_application_unsigned(&apdu[0],
|
||||
ac_descr[object_index].assigned_access_rights_count);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0;
|
||||
i < ac_descr[object_index].assigned_access_rights_count;
|
||||
i++) {
|
||||
len = bacapp_encode_assigned_access_rights(&apdu[0],
|
||||
&ac_descr[object_index].assigned_access_rights[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <=
|
||||
ac_descr[object_index].assigned_access_rights_count) {
|
||||
apdu_len = bacapp_encode_assigned_access_rights(&apdu[0],
|
||||
&ac_descr[object_index]
|
||||
.assigned_access_rights[rpdata->array_index - 1]);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) &&
|
||||
(rpdata->object_property != PROP_AUTHENTICATION_FACTORS) &&
|
||||
(rpdata->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_Credential_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
unsigned object_index = 0;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_AUTHENTICATION_FACTORS) &&
|
||||
(wp_data->object_property != PROP_ASSIGNED_ACCESS_RIGHTS) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index =
|
||||
Access_Credential_Instance_To_Index(wp_data->object_instance);
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
ac_descr[object_index].global_identifier =
|
||||
value.type.Unsigned_Int;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_CREDENTIAL_STATUS:
|
||||
case PROP_REASON_FOR_DISABLE:
|
||||
case PROP_AUTHENTICATION_FACTORS:
|
||||
case PROP_ACTIVATION_TIME:
|
||||
case PROP_EXPIRATION_TIME:
|
||||
case PROP_CREDENTIAL_DISABLE:
|
||||
case PROP_ASSIGNED_ACCESS_RIGHTS:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessCredential(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_Credential_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_CREDENTIAL;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_Credential_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_CREDENTIAL
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access Credential", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessCredential);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_CREDENTIAL */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,124 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_CREDENTIAL_H
|
||||
#define ACCESS_CREDENTIAL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/datetime.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#include "bacnet/assigned_access_rights.h"
|
||||
#include "bacnet/credential_authentication_factor.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_CREDENTIALS
|
||||
#define MAX_ACCESS_CREDENTIALS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_REASONS_FOR_DISABLE
|
||||
#define MAX_REASONS_FOR_DISABLE 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_AUTHENTICATION_FACTORS
|
||||
#define MAX_AUTHENTICATION_FACTORS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_ASSIGNED_ACCESS_RIGHTS
|
||||
#define MAX_ASSIGNED_ACCESS_RIGHTS 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t global_identifier;
|
||||
BACNET_RELIABILITY reliability;
|
||||
bool credential_status;
|
||||
uint32_t reasons_count;
|
||||
BACNET_ACCESS_CREDENTIAL_DISABLE_REASON
|
||||
reason_for_disable[MAX_REASONS_FOR_DISABLE];
|
||||
uint32_t auth_factors_count;
|
||||
BACNET_CREDENTIAL_AUTHENTICATION_FACTOR
|
||||
auth_factors[MAX_AUTHENTICATION_FACTORS];
|
||||
BACNET_DATE_TIME activation_time, expiration_time;
|
||||
BACNET_ACCESS_CREDENTIAL_DISABLE credential_disable;
|
||||
uint32_t assigned_access_rights_count;
|
||||
BACNET_ASSIGNED_ACCESS_RIGHTS
|
||||
assigned_access_rights[MAX_ASSIGNED_ACCESS_RIGHTS];
|
||||
} ACCESS_CREDENTIAL_DESCR;
|
||||
|
||||
void Access_Credential_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_Credential_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Credential_Count(
|
||||
void);
|
||||
uint32_t Access_Credential_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_Credential_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_Credential_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
|
||||
bool Access_Credential_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_Credential_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
int Access_Credential_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_Credential_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_Credential_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_Credential_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_Credential_Cleanup(
|
||||
void);
|
||||
void Access_Credential_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessCredential(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_CREDENTIAL
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_credential.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/assigned_access_rights.c \
|
||||
$(SRC_DIR)/bacnet/authentication_factor.c \
|
||||
$(SRC_DIR)/bacnet/credential_authentication_factor.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_credential
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,632 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access Door Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "access_door.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_Door_Initialized = false;
|
||||
|
||||
static ACCESS_DOOR_DESCR ad_descr[MAX_ACCESS_DOORS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
||||
PROP_EVENT_STATE, PROP_RELIABILITY, PROP_OUT_OF_SERVICE,
|
||||
PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, PROP_DOOR_PULSE_TIME,
|
||||
PROP_DOOR_EXTENDED_PULSE_TIME, PROP_DOOR_OPEN_TOO_LONG_TIME, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { PROP_DOOR_STATUS, PROP_LOCK_STATUS,
|
||||
PROP_SECURED_STATUS, PROP_DOOR_UNLOCK_DELAY_TIME, PROP_DOOR_ALARM_STATE,
|
||||
-1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_Door_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_Door_Init(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
if (!Access_Door_Initialized) {
|
||||
Access_Door_Initialized = true;
|
||||
|
||||
/* initialize all the access door priority arrays to NULL */
|
||||
for (i = 0; i < MAX_ACCESS_DOORS; i++) {
|
||||
ad_descr[i].relinquish_default = DOOR_VALUE_LOCK;
|
||||
ad_descr[i].event_state = EVENT_STATE_NORMAL;
|
||||
ad_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
ad_descr[i].out_of_service = false;
|
||||
ad_descr[i].door_status = DOOR_STATUS_CLOSED;
|
||||
ad_descr[i].lock_status = LOCK_STATUS_LOCKED;
|
||||
ad_descr[i].secured_status = DOOR_SECURED_STATUS_SECURED;
|
||||
ad_descr[i].door_pulse_time = 30; /* 3s */
|
||||
ad_descr[i].door_extended_pulse_time = 50; /* 5s */
|
||||
ad_descr[i].door_unlock_delay_time = 0; /* 0s */
|
||||
ad_descr[i].door_open_too_long_time = 300; /* 30s */
|
||||
ad_descr[i].door_alarm_state = DOOR_ALARM_STATE_NORMAL;
|
||||
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
|
||||
ad_descr[i].value_active[j] = false;
|
||||
/* just to fill in */
|
||||
ad_descr[i].priority_array[j] = DOOR_VALUE_LOCK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_Door_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_DOORS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_Door_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_DOORS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_Door_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_Door_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_DOORS;
|
||||
|
||||
if (object_instance < MAX_ACCESS_DOORS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
BACNET_DOOR_VALUE Access_Door_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
unsigned i = 0;
|
||||
BACNET_DOOR_VALUE value = DOOR_VALUE_LOCK;
|
||||
|
||||
index = Access_Door_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
value = ad_descr[i].relinquish_default;
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (ad_descr[index].value_active[i]) {
|
||||
value = ad_descr[index].priority_array[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned Access_Door_Present_Value_Priority(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0; /* instance to index conversion */
|
||||
unsigned i = 0; /* loop counter */
|
||||
unsigned priority = 0; /* return value */
|
||||
|
||||
index = Access_Door_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (ad_descr[index].value_active[i]) {
|
||||
priority = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
bool Access_Door_Present_Value_Set(
|
||||
uint32_t object_instance, BACNET_DOOR_VALUE value, unsigned priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
index = Access_Door_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */) && (value >= DOOR_VALUE_LOCK) &&
|
||||
(value <= DOOR_VALUE_EXTENDED_PULSE_UNLOCK)) {
|
||||
ad_descr[index].value_active[priority - 1] = true;
|
||||
ad_descr[index].priority_array[priority - 1] = value;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Access_Door_Present_Value_Relinquish(
|
||||
uint32_t object_instance, unsigned priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
index = Access_Door_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */)) {
|
||||
ad_descr[index].value_active[priority - 1] = false;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
BACNET_DOOR_VALUE Access_Door_Relinquish_Default(uint32_t object_instance)
|
||||
{
|
||||
BACNET_DOOR_VALUE status = -1;
|
||||
unsigned index = 0;
|
||||
index = Access_Door_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
return ad_descr[index].relinquish_default;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_Door_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_DOORS) {
|
||||
sprintf(text_string, "ACCESS DOOR %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Access_Door_Out_Of_Service(uint32_t instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool oos_flag = false;
|
||||
|
||||
index = Access_Door_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
oos_flag = ad_descr[index].out_of_service;
|
||||
}
|
||||
|
||||
return oos_flag;
|
||||
}
|
||||
|
||||
void Access_Door_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Access_Door_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_DOORS) {
|
||||
ad_descr[index].out_of_service = oos_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_Door_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_Door_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_DOOR, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_Door_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_DOOR);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], Access_Door_Present_Value(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Access_Door_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].event_state);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Access_Door_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
/* Array element zero is the number of elements in the array */
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
||||
/* if no index was specified, then try to encode the entire list
|
||||
*/
|
||||
/* into one packet. */
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
/* FIXME: check if we have room before adding it to APDU */
|
||||
if (ad_descr[object_index].value_active[i]) {
|
||||
len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
len = encode_application_enumerated(&apdu[apdu_len],
|
||||
ad_descr[object_index].priority_array[i]);
|
||||
}
|
||||
/* add it if we have room */
|
||||
if ((apdu_len + len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
|
||||
if (ad_descr[object_index].value_active[i]) {
|
||||
apdu_len = encode_application_null(&apdu[0]);
|
||||
} else {
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[apdu_len],
|
||||
ad_descr[object_index].priority_array[i]);
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
apdu_len = encode_application_enumerated(&apdu[0],
|
||||
Access_Door_Relinquish_Default(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_DOOR_STATUS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].door_status);
|
||||
break;
|
||||
case PROP_LOCK_STATUS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].lock_status);
|
||||
break;
|
||||
case PROP_SECURED_STATUS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].secured_status);
|
||||
break;
|
||||
case PROP_DOOR_PULSE_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ad_descr[object_index].door_pulse_time);
|
||||
break;
|
||||
case PROP_DOOR_EXTENDED_PULSE_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ad_descr[object_index].door_extended_pulse_time);
|
||||
break;
|
||||
case PROP_DOOR_UNLOCK_DELAY_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ad_descr[object_index].door_unlock_delay_time);
|
||||
break;
|
||||
case PROP_DOOR_OPEN_TOO_LONG_TIME:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ad_descr[object_index].door_open_too_long_time);
|
||||
break;
|
||||
case PROP_DOOR_ALARM_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ad_descr[object_index].door_alarm_state);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_Door_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
unsigned object_index = 0;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index = Access_Door_Instance_To_Index(wp_data->object_instance);
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
status = Access_Door_Present_Value_Set(wp_data->object_instance,
|
||||
value.type.Enumerated, wp_data->priority);
|
||||
if (wp_data->priority == 6) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
status = Access_Door_Present_Value_Relinquish(
|
||||
wp_data->object_instance, wp_data->priority);
|
||||
if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
Access_Door_Out_Of_Service_Set(
|
||||
wp_data->object_instance, value.type.Boolean);
|
||||
}
|
||||
break;
|
||||
case PROP_DOOR_STATUS:
|
||||
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
ad_descr[object_index].door_status = value.type.Enumerated;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
break;
|
||||
case PROP_LOCK_STATUS:
|
||||
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
ad_descr[object_index].lock_status = value.type.Enumerated;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
break;
|
||||
case PROP_DOOR_ALARM_STATE:
|
||||
if (Access_Door_Out_Of_Service(wp_data->object_instance)) {
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
ad_descr[object_index].door_alarm_state =
|
||||
value.type.Enumerated;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
case PROP_SECURED_STATUS:
|
||||
case PROP_DOOR_PULSE_TIME:
|
||||
case PROP_DOOR_EXTENDED_PULSE_TIME:
|
||||
case PROP_DOOR_UNLOCK_DELAY_TIME:
|
||||
case PROP_DOOR_OPEN_TOO_LONG_TIME:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessDoor(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_Door_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_DOOR;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_Door_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_DOOR
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access Door", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessDoor);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_DOOR */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,143 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_DOOR_H
|
||||
#define ACCESS_DOOR_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_DOORS
|
||||
#define MAX_ACCESS_DOORS 4
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
bool value_active[BACNET_MAX_PRIORITY];
|
||||
BACNET_DOOR_VALUE priority_array[BACNET_MAX_PRIORITY];
|
||||
BACNET_DOOR_VALUE relinquish_default;
|
||||
BACNET_EVENT_STATE event_state;
|
||||
BACNET_RELIABILITY reliability;
|
||||
bool out_of_service;
|
||||
BACNET_DOOR_STATUS door_status;
|
||||
BACNET_LOCK_STATUS lock_status;
|
||||
BACNET_DOOR_SECURED_STATUS secured_status;
|
||||
uint32_t door_pulse_time, door_extended_pulse_time,
|
||||
door_unlock_delay_time, door_open_too_long_time;
|
||||
BACNET_DOOR_ALARM_STATE door_alarm_state;
|
||||
} ACCESS_DOOR_DESCR;
|
||||
|
||||
void Access_Door_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_Door_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Door_Count(
|
||||
void);
|
||||
uint32_t Access_Door_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_Door_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_Door_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
BACNET_DOOR_VALUE Access_Door_Present_Value(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Door_Present_Value_Priority(
|
||||
uint32_t object_instance);
|
||||
bool Access_Door_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_DOOR_VALUE value,
|
||||
unsigned priority);
|
||||
bool Access_Door_Present_Value_Relinquish(
|
||||
uint32_t object_instance,
|
||||
unsigned priority);
|
||||
|
||||
BACNET_DOOR_VALUE Access_Door_Relinquish_Default(
|
||||
uint32_t object_instance);
|
||||
bool Access_Door_Relinquish_Default_Set(
|
||||
uint32_t object_instance,
|
||||
float value);
|
||||
|
||||
bool Access_Door_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Access_Door_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
bool Access_Door_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
|
||||
bool Access_Door_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_Door_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Access_Door_Description(
|
||||
uint32_t instance);
|
||||
bool Access_Door_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
bool Access_Door_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Access_Door_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool oos_flag);
|
||||
|
||||
int Access_Door_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_Door_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_Door_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_Door_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_Door_Cleanup(
|
||||
void);
|
||||
void Access_Door_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessDoor(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_DOOR
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_door.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_door
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,431 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access Point Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "access_point.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_Point_Initialized = false;
|
||||
|
||||
static ACCESS_POINT_DESCR ap_descr[MAX_ACCESS_POINTS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_STATUS_FLAGS, PROP_EVENT_STATE,
|
||||
PROP_RELIABILITY, PROP_OUT_OF_SERVICE, PROP_AUTHENTICATION_STATUS,
|
||||
PROP_ACTIVE_AUTHENTICATION_POLICY, PROP_NUMBER_OF_AUTHENTICATION_POLICIES,
|
||||
PROP_AUTHORIZATION_MODE, PROP_ACCESS_EVENT, PROP_ACCESS_EVENT_TAG,
|
||||
PROP_ACCESS_EVENT_TIME, PROP_ACCESS_EVENT_CREDENTIAL, PROP_ACCESS_DOORS,
|
||||
PROP_PRIORITY_FOR_WRITING, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_Point_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_Point_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Access_Point_Initialized) {
|
||||
Access_Point_Initialized = true;
|
||||
|
||||
for (i = 0; i < MAX_ACCESS_POINTS; i++) {
|
||||
ap_descr[i].event_state = EVENT_STATE_NORMAL;
|
||||
ap_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
ap_descr[i].out_of_service = false;
|
||||
ap_descr[i].authentication_status = AUTHENTICATION_STATUS_NOT_READY;
|
||||
ap_descr[i].active_authentication_policy = 0;
|
||||
ap_descr[i].number_of_authentication_policies = 0;
|
||||
ap_descr[i].authorization_mode = AUTHORIZATION_MODE_AUTHORIZE;
|
||||
ap_descr[i].access_event = ACCESS_EVENT_NONE;
|
||||
/* timestamp uninitialized */
|
||||
/* access_event_credential should be set to some meaningful value */
|
||||
ap_descr[i].num_doors = 0;
|
||||
/* fill in the access doors with proper ids */
|
||||
ap_descr[i].priority_for_writing = 16; /* lowest possible for now */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_Point_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_POINTS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_Point_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_POINTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_Point_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_Point_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_POINTS;
|
||||
|
||||
if (object_instance < MAX_ACCESS_POINTS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_Point_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_POINTS) {
|
||||
sprintf(
|
||||
text_string, "ACCESS POINT %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Access_Point_Out_Of_Service(uint32_t instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool oos_flag = false;
|
||||
|
||||
index = Access_Point_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_POINTS) {
|
||||
oos_flag = ap_descr[index].out_of_service;
|
||||
}
|
||||
|
||||
return oos_flag;
|
||||
}
|
||||
|
||||
void Access_Point_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Access_Point_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_POINTS) {
|
||||
ap_descr[index].out_of_service = oos_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_Point_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_Point_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_POINT, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_Point_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_POINT);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Access_Point_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ap_descr[object_index].event_state);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ap_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Access_Point_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_AUTHENTICATION_STATUS:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ap_descr[object_index].authentication_status);
|
||||
break;
|
||||
case PROP_ACTIVE_AUTHENTICATION_POLICY:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ap_descr[object_index].active_authentication_policy);
|
||||
break;
|
||||
case PROP_NUMBER_OF_AUTHENTICATION_POLICIES:
|
||||
apdu_len = encode_application_unsigned(&apdu[0],
|
||||
ap_descr[object_index].number_of_authentication_policies);
|
||||
break;
|
||||
case PROP_AUTHORIZATION_MODE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ap_descr[object_index].authorization_mode);
|
||||
break;
|
||||
case PROP_ACCESS_EVENT:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ap_descr[object_index].access_event);
|
||||
break;
|
||||
case PROP_ACCESS_EVENT_TAG:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ap_descr[object_index].access_event_tag);
|
||||
break;
|
||||
case PROP_ACCESS_EVENT_TIME:
|
||||
apdu_len = bacapp_encode_timestamp(
|
||||
&apdu[0], &ap_descr[object_index].access_event_time);
|
||||
break;
|
||||
case PROP_ACCESS_EVENT_CREDENTIAL:
|
||||
apdu_len = bacapp_encode_device_obj_ref(
|
||||
&apdu[0], &ap_descr[object_index].access_event_credential);
|
||||
break;
|
||||
case PROP_ACCESS_DOORS:
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ap_descr[object_index].num_doors);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0; i < ap_descr[object_index].num_doors; i++) {
|
||||
len = bacapp_encode_device_obj_ref(
|
||||
&apdu[0], &ap_descr[object_index].access_doors[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <= ap_descr[object_index].num_doors) {
|
||||
apdu_len = bacapp_encode_device_obj_ref(&apdu[0],
|
||||
&ap_descr[object_index]
|
||||
.access_doors[rpdata->array_index - 1]);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_ACCESS_DOORS) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_ACCESS_DOORS) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
case PROP_AUTHENTICATION_STATUS:
|
||||
case PROP_ACTIVE_AUTHENTICATION_POLICY:
|
||||
case PROP_NUMBER_OF_AUTHENTICATION_POLICIES:
|
||||
case PROP_AUTHORIZATION_MODE:
|
||||
case PROP_ACCESS_EVENT:
|
||||
case PROP_ACCESS_EVENT_TAG:
|
||||
case PROP_ACCESS_EVENT_TIME:
|
||||
case PROP_ACCESS_EVENT_CREDENTIAL:
|
||||
case PROP_ACCESS_DOORS:
|
||||
case PROP_PRIORITY_FOR_WRITING:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessPoint(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_Point_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_POINT;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_Point_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_POINT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access Point", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessPoint);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_POINT */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,120 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_POINT_H
|
||||
#define ACCESS_POINT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_POINTS
|
||||
#define MAX_ACCESS_POINTS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_ACCESS_DOORS_COUNT
|
||||
#define MAX_ACCESS_DOORS_COUNT 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
BACNET_EVENT_STATE event_state;
|
||||
BACNET_RELIABILITY reliability;
|
||||
bool out_of_service;
|
||||
BACNET_AUTHENTICATION_STATUS authentication_status;
|
||||
uint32_t active_authentication_policy,
|
||||
number_of_authentication_policies;
|
||||
BACNET_AUTHORIZATION_MODE authorization_mode;
|
||||
BACNET_ACCESS_EVENT access_event;
|
||||
uint32_t access_event_tag;
|
||||
BACNET_TIMESTAMP access_event_time;
|
||||
BACNET_DEVICE_OBJECT_REFERENCE access_event_credential;
|
||||
uint32_t num_doors; /* helper value, not a property */
|
||||
BACNET_DEVICE_OBJECT_REFERENCE access_doors[MAX_ACCESS_DOORS_COUNT];
|
||||
uint8_t priority_for_writing;
|
||||
} ACCESS_POINT_DESCR;
|
||||
|
||||
void Access_Point_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_Point_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Point_Count(
|
||||
void);
|
||||
uint32_t Access_Point_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_Point_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_Point_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
|
||||
bool Access_Point_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_Point_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
|
||||
bool Access_Point_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Access_Point_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool oos_flag);
|
||||
|
||||
int Access_Point_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_Point_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_Point_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_Point_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_Point_Cleanup(
|
||||
void);
|
||||
void Access_Point_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessPoint(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_POINT
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_point.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(SRC_DIR)/bacnet/timestamp.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_point
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,404 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access Rights Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "access_rights.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_Rights_Initialized = false;
|
||||
|
||||
static ACCESS_RIGHTS_DESCR ar_descr[MAX_ACCESS_RIGHTSS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
|
||||
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_ENABLE,
|
||||
PROP_NEGATIVE_ACCESS_RULES, PROP_POSITIVE_ACCESS_RULES, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_Rights_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_Rights_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Access_Rights_Initialized) {
|
||||
Access_Rights_Initialized = true;
|
||||
|
||||
for (i = 0; i < MAX_ACCESS_RIGHTSS; i++) {
|
||||
ar_descr[i].global_identifier =
|
||||
0; /* set to some meaningful value */
|
||||
ar_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
ar_descr[i].enable = false;
|
||||
ar_descr[i].negative_access_rules_count = 0;
|
||||
ar_descr[i].positive_access_rules_count = 0;
|
||||
/* fill in the positive and negative access rules with proper ids */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_Rights_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_RIGHTSS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_Rights_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_RIGHTSS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_Rights_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_Rights_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_RIGHTSS;
|
||||
|
||||
if (object_instance < MAX_ACCESS_RIGHTSS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_Rights_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_RIGHTSS) {
|
||||
sprintf(
|
||||
text_string, "ACCESS RIGHTS %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_Rights_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_Rights_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_RIGHTS, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_Rights_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_RIGHTS);
|
||||
break;
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], ar_descr[object_index].global_identifier);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], ar_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_ENABLE:
|
||||
apdu_len = encode_application_boolean(
|
||||
&apdu[0], ar_descr[object_index].enable);
|
||||
break;
|
||||
case PROP_NEGATIVE_ACCESS_RULES:
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len = encode_application_unsigned(&apdu[0],
|
||||
ar_descr[object_index].negative_access_rules_count);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0;
|
||||
i < ar_descr[object_index].negative_access_rules_count;
|
||||
i++) {
|
||||
len = bacapp_encode_access_rule(&apdu[0],
|
||||
&ar_descr[object_index].negative_access_rules[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <=
|
||||
ar_descr[object_index].negative_access_rules_count) {
|
||||
apdu_len = bacapp_encode_access_rule(&apdu[0],
|
||||
&ar_descr[object_index]
|
||||
.negative_access_rules[rpdata->array_index - 1]);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_POSITIVE_ACCESS_RULES:
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len = encode_application_unsigned(&apdu[0],
|
||||
ar_descr[object_index].positive_access_rules_count);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
for (i = 0;
|
||||
i < ar_descr[object_index].positive_access_rules_count;
|
||||
i++) {
|
||||
len = bacapp_encode_access_rule(&apdu[0],
|
||||
&ar_descr[object_index].positive_access_rules[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index <=
|
||||
ar_descr[object_index].positive_access_rules_count) {
|
||||
apdu_len = bacapp_encode_access_rule(&apdu[0],
|
||||
&ar_descr[object_index]
|
||||
.positive_access_rules[rpdata->array_index - 1]);
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) &&
|
||||
(rpdata->object_property != PROP_NEGATIVE_ACCESS_RULES) &&
|
||||
(rpdata->object_property != PROP_POSITIVE_ACCESS_RULES) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_Rights_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
unsigned object_index = 0;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_NEGATIVE_ACCESS_RULES) &&
|
||||
(wp_data->object_property != PROP_POSITIVE_ACCESS_RULES) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index = Access_Rights_Instance_To_Index(wp_data->object_instance);
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
ar_descr[object_index].global_identifier =
|
||||
value.type.Unsigned_Int;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_ENABLE:
|
||||
case PROP_NEGATIVE_ACCESS_RULES:
|
||||
case PROP_POSITIVE_ACCESS_RULES:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessRights(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_Rights_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_RIGHTS;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_Rights_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_RIGHTS
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access Rights", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessRights);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_RIGHTS */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,110 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_RIGHTS_H
|
||||
#define ACCESS_RIGHTS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#include "bacnet/access_rule.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_RIGHTSS
|
||||
#define MAX_ACCESS_RIGHTSS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_NEGATIVE_ACCESS_RIGHTS_RULES
|
||||
#define MAX_NEGATIVE_ACCESS_RIGHTS_RULES 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_POSITIVE_ACCESS_RIGHTS_RULES
|
||||
#define MAX_POSITIVE_ACCESS_RIGHTS_RULES 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t global_identifier;
|
||||
BACNET_RELIABILITY reliability;
|
||||
bool enable;
|
||||
uint32_t negative_access_rules_count, positive_access_rules_count;
|
||||
BACNET_ACCESS_RULE
|
||||
negative_access_rules[MAX_NEGATIVE_ACCESS_RIGHTS_RULES];
|
||||
BACNET_ACCESS_RULE
|
||||
positive_access_rules[MAX_POSITIVE_ACCESS_RIGHTS_RULES];
|
||||
} ACCESS_RIGHTS_DESCR;
|
||||
|
||||
void Access_Rights_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_Rights_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Rights_Count(
|
||||
void);
|
||||
uint32_t Access_Rights_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_Rights_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_Rights_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Access_Rights_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_Rights_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
int Access_Rights_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_Rights_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_Rights_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_Rights_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_Rights_Cleanup(
|
||||
void);
|
||||
void Access_Rights_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessRights(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_RIGHTS
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_rights.c \
|
||||
$(SRC_DIR)/bacnet/access_rule.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_rights
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,346 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access User Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "access_user.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_User_Initialized = false;
|
||||
|
||||
static ACCESS_USER_DESCR au_descr[MAX_ACCESS_USERS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
|
||||
PROP_STATUS_FLAGS, PROP_RELIABILITY, PROP_USER_TYPE, PROP_CREDENTIALS, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_User_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_User_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Access_User_Initialized) {
|
||||
Access_User_Initialized = true;
|
||||
|
||||
for (i = 0; i < MAX_ACCESS_USERS; i++) {
|
||||
au_descr[i].global_identifier =
|
||||
0; /* set to some meaningful value */
|
||||
au_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
au_descr[i].user_type = ACCESS_USER_TYPE_PERSON;
|
||||
au_descr[i].credentials_count = 0;
|
||||
/* fill in the credentials with proper ids */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_User_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_USERS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_User_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_USERS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_User_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_User_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_USERS;
|
||||
|
||||
if (object_instance < MAX_ACCESS_USERS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_User_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_USERS) {
|
||||
sprintf(text_string, "ACCESS USER %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_User_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_User_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_USER, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_User_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_USER);
|
||||
break;
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], au_descr[object_index].global_identifier);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], au_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_USER_TYPE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], au_descr[object_index].user_type);
|
||||
break;
|
||||
case PROP_CREDENTIALS:
|
||||
for (i = 0; i < au_descr[object_index].credentials_count; i++) {
|
||||
len = bacapp_encode_device_obj_ref(
|
||||
&apdu[0], &au_descr[object_index].credentials[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_User_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
unsigned object_index = 0;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index = Access_User_Instance_To_Index(wp_data->object_instance);
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
au_descr[object_index].global_identifier =
|
||||
value.type.Unsigned_Int;
|
||||
}
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_USER_TYPE:
|
||||
case PROP_CREDENTIALS:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessUser(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_User_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_USER;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_User_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_USER
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access User", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessUser);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_USER */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,103 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_USER_H
|
||||
#define ACCESS_USER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_USERS
|
||||
#define MAX_ACCESS_USERS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_ACCESS_USER_CREDENTIALS_COUNT
|
||||
#define MAX_ACCESS_USER_CREDENTIALS_COUNT 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t global_identifier;
|
||||
BACNET_RELIABILITY reliability;
|
||||
BACNET_ACCESS_USER_TYPE user_type;
|
||||
uint32_t credentials_count;
|
||||
BACNET_DEVICE_OBJECT_REFERENCE
|
||||
credentials[MAX_ACCESS_USER_CREDENTIALS_COUNT];
|
||||
} ACCESS_USER_DESCR;
|
||||
|
||||
void Access_User_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_User_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_User_Count(
|
||||
void);
|
||||
uint32_t Access_User_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_User_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_User_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Access_User_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_User_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
int Access_User_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_User_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_User_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_User_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_User_Cleanup(
|
||||
void);
|
||||
void Access_User_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessUser(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_USER
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_user.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_user
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,411 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Access Zone Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "access_zone.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
static bool Access_Zone_Initialized = false;
|
||||
|
||||
static ACCESS_ZONE_DESCR az_descr[MAX_ACCESS_ZONES];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_GLOBAL_IDENTIFIER,
|
||||
PROP_OCCUPANCY_STATE, PROP_STATUS_FLAGS, PROP_EVENT_STATE, PROP_RELIABILITY,
|
||||
PROP_OUT_OF_SERVICE, PROP_ENTRY_POINTS, PROP_EXIT_POINTS, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Access_Zone_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Access_Zone_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!Access_Zone_Initialized) {
|
||||
Access_Zone_Initialized = true;
|
||||
|
||||
for (i = 0; i < MAX_ACCESS_ZONES; i++) {
|
||||
az_descr[i].global_identifier =
|
||||
0; /* set to some meaningful value */
|
||||
az_descr[i].occupancy_state = ACCESS_ZONE_OCCUPANCY_STATE_DISABLED;
|
||||
az_descr[i].event_state = EVENT_STATE_NORMAL;
|
||||
az_descr[i].reliability = RELIABILITY_NO_FAULT_DETECTED;
|
||||
az_descr[i].out_of_service = false;
|
||||
az_descr[i].entry_points_count = 0;
|
||||
az_descr[i].exit_points_count = 0;
|
||||
/* fill in the entry points and exit points with proper ids */
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Access_Zone_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ACCESS_ZONES) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Access_Zone_Count(void)
|
||||
{
|
||||
return MAX_ACCESS_ZONES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Access_Zone_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Access_Zone_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ACCESS_ZONES;
|
||||
|
||||
if (object_instance < MAX_ACCESS_ZONES) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Access_Zone_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ACCESS_ZONES) {
|
||||
sprintf(text_string, "ACCESS ZONE %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Access_Zone_Out_Of_Service(uint32_t instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool oos_flag = false;
|
||||
|
||||
index = Access_Zone_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_ZONES) {
|
||||
oos_flag = az_descr[index].out_of_service;
|
||||
}
|
||||
|
||||
return oos_flag;
|
||||
}
|
||||
|
||||
void Access_Zone_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Access_Zone_Instance_To_Index(instance);
|
||||
if (index < MAX_ACCESS_ZONES) {
|
||||
az_descr[index].out_of_service = oos_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Access_Zone_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
object_index = Access_Zone_Instance_To_Index(rpdata->object_instance);
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ACCESS_ZONE, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Access_Zone_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ACCESS_ZONE);
|
||||
break;
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], az_descr[object_index].global_identifier);
|
||||
break;
|
||||
case PROP_OCCUPANCY_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], az_descr[object_index].occupancy_state);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Access_Zone_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], az_descr[object_index].event_state);
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], az_descr[object_index].reliability);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Access_Zone_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_ENTRY_POINTS:
|
||||
for (i = 0; i < az_descr[object_index].entry_points_count; i++) {
|
||||
len = bacapp_encode_device_obj_ref(
|
||||
&apdu[0], &az_descr[object_index].entry_points[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_EXIT_POINTS:
|
||||
for (i = 0; i < az_descr[object_index].exit_points_count; i++) {
|
||||
len = bacapp_encode_device_obj_ref(
|
||||
&apdu[0], &az_descr[object_index].exit_points[i]);
|
||||
if (apdu_len + len < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Access_Zone_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
unsigned object_index = 0;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index = Access_Zone_Instance_To_Index(wp_data->object_instance);
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_GLOBAL_IDENTIFIER:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
az_descr[object_index].global_identifier =
|
||||
value.type.Unsigned_Int;
|
||||
}
|
||||
break;
|
||||
case PROP_RELIABILITY:
|
||||
if (Access_Zone_Out_Of_Service(wp_data->object_instance)) {
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
az_descr[object_index].reliability = value.type.Enumerated;
|
||||
}
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_OCCUPANCY_STATE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
case PROP_ENTRY_POINTS:
|
||||
case PROP_EXIT_POINTS:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testAccessZone(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Access_Zone_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ACCESS_ZONE;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Access_Zone_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ACCESS_ZONE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Access Zone", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAccessZone);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ACCESS_ZONE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,117 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2015 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef ACCESS_ZONE_H
|
||||
#define ACCESS_ZONE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/bacdevobjpropref.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
|
||||
#ifndef MAX_ACCESS_ZONES
|
||||
#define MAX_ACCESS_ZONES 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_ACCESS_ZONE_ENTRY_POINTS
|
||||
#define MAX_ACCESS_ZONE_ENTRY_POINTS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_ACCESS_ZONE_EXIT_POINTS
|
||||
#define MAX_ACCESS_ZONE_EXIT_POINTS 4
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct {
|
||||
uint32_t global_identifier;
|
||||
BACNET_ACCESS_ZONE_OCCUPANCY_STATE occupancy_state;
|
||||
BACNET_EVENT_STATE event_state;
|
||||
BACNET_RELIABILITY reliability;
|
||||
bool out_of_service;
|
||||
uint32_t entry_points_count, exit_points_count;
|
||||
BACNET_DEVICE_OBJECT_REFERENCE
|
||||
entry_points[MAX_ACCESS_ZONE_ENTRY_POINTS];
|
||||
BACNET_DEVICE_OBJECT_REFERENCE
|
||||
exit_points[MAX_ACCESS_ZONE_EXIT_POINTS];
|
||||
} ACCESS_ZONE_DESCR;
|
||||
|
||||
void Access_Zone_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Access_Zone_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Access_Zone_Count(
|
||||
void);
|
||||
uint32_t Access_Zone_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Access_Zone_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Access_Zone_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Access_Zone_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Access_Zone_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
bool Access_Zone_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Access_Zone_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool oos_flag);
|
||||
|
||||
int Access_Zone_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Access_Zone_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Access_Zone_Create(
|
||||
uint32_t object_instance);
|
||||
bool Access_Zone_Delete(
|
||||
uint32_t object_instance);
|
||||
void Access_Zone_Cleanup(
|
||||
void);
|
||||
void Access_Zone_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAccessZone(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ACCESS_ZONE
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = access_zone.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = access_zone
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,174 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef AI_H
|
||||
#define AI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
#include "bacnet/basic/object/nc.h"
|
||||
#include "bacnet/getevent.h"
|
||||
#include "bacnet/alarm_ack.h"
|
||||
#include "bacnet/get_alarm_sum.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct analog_input_descr {
|
||||
unsigned Event_State:3;
|
||||
float Present_Value;
|
||||
BACNET_RELIABILITY Reliability;
|
||||
bool Out_Of_Service;
|
||||
uint8_t Units;
|
||||
float Prior_Value;
|
||||
float COV_Increment;
|
||||
bool Changed;
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
uint32_t Time_Delay;
|
||||
uint32_t Notification_Class;
|
||||
float High_Limit;
|
||||
float Low_Limit;
|
||||
float Deadband;
|
||||
unsigned Limit_Enable:2;
|
||||
unsigned Event_Enable:3;
|
||||
unsigned Notify_Type:1;
|
||||
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
|
||||
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
|
||||
/* time to generate event notification */
|
||||
uint32_t Remaining_Time_Delay;
|
||||
/* AckNotification informations */
|
||||
ACK_NOTIFICATION Ack_notify_data;
|
||||
#endif
|
||||
} ANALOG_INPUT_DESCR;
|
||||
|
||||
void Analog_Input_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Analog_Input_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Input_Count(
|
||||
void);
|
||||
uint32_t Analog_Input_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Analog_Input_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Analog_Input_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Analog_Input_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Analog_Input_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Analog_Input_Description(
|
||||
uint32_t instance);
|
||||
bool Analog_Input_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
bool Analog_Input_Units_Set(
|
||||
uint32_t instance,
|
||||
uint16_t units);
|
||||
uint16_t Analog_Input_Units(
|
||||
uint32_t instance);
|
||||
|
||||
int Analog_Input_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Analog_Input_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
float Analog_Input_Present_Value(
|
||||
uint32_t object_instance);
|
||||
void Analog_Input_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value);
|
||||
|
||||
bool Analog_Input_Out_Of_Service(
|
||||
uint32_t object_instance);
|
||||
void Analog_Input_Out_Of_Service_Set(
|
||||
uint32_t object_instance,
|
||||
bool oos_flag);
|
||||
|
||||
bool Analog_Input_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Analog_Input_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
bool Analog_Input_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
float Analog_Input_COV_Increment(
|
||||
uint32_t instance);
|
||||
void Analog_Input_COV_Increment_Set(
|
||||
uint32_t instance,
|
||||
float value);
|
||||
|
||||
/* note: header of Intrinsic_Reporting function is required
|
||||
even when INTRINSIC_REPORTING is not defined */
|
||||
void Analog_Input_Intrinsic_Reporting(
|
||||
uint32_t object_instance);
|
||||
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
int Analog_Input_Event_Information(
|
||||
unsigned index,
|
||||
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
|
||||
|
||||
int Analog_Input_Alarm_Ack(
|
||||
BACNET_ALARM_ACK_DATA * alarmack_data,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
int Analog_Input_Alarm_Summary(
|
||||
unsigned index,
|
||||
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
|
||||
#endif
|
||||
|
||||
bool Analog_Input_Create(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Input_Delete(
|
||||
uint32_t object_instance);
|
||||
void Analog_Input_Cleanup(
|
||||
void);
|
||||
void Analog_Input_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAnalogInput(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
HANDLER_DIR = ../handler
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR)
|
||||
DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_ANALOG_INPUT
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = ai.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = analog_input
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,558 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Analog Output Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/ao.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#ifndef MAX_ANALOG_OUTPUTS
|
||||
#define MAX_ANALOG_OUTPUTS 4
|
||||
#endif
|
||||
|
||||
/* we choose to have a NULL level in our system represented by */
|
||||
/* a particular value. When the priorities are not in use, they */
|
||||
/* will be relinquished (i.e. set to the NULL level). */
|
||||
#define AO_LEVEL_NULL 255
|
||||
/* When all the priorities are level null, the present value returns */
|
||||
/* the Relinquish Default value */
|
||||
#define AO_RELINQUISH_DEFAULT 0
|
||||
/* Here is our Priority Array. They are supposed to be Real, but */
|
||||
/* we don't have that kind of memory, so we will use a single byte */
|
||||
/* and load a Real for returning the value when asked. */
|
||||
static uint8_t Analog_Output_Level[MAX_ANALOG_OUTPUTS][BACNET_MAX_PRIORITY];
|
||||
/* Writable out-of-service allows others to play with our Present Value */
|
||||
/* without changing the physical output */
|
||||
static bool Out_Of_Service[MAX_ANALOG_OUTPUTS];
|
||||
|
||||
/* we need to have our arrays initialized before answering any calls */
|
||||
static bool Analog_Output_Initialized = false;
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
||||
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_UNITS, PROP_PRIORITY_ARRAY,
|
||||
PROP_RELINQUISH_DEFAULT, -1 };
|
||||
|
||||
static const int Properties_Optional[] = { -1 };
|
||||
|
||||
static const int Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Analog_Output_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Analog_Output_Init(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
if (!Analog_Output_Initialized) {
|
||||
Analog_Output_Initialized = true;
|
||||
|
||||
/* initialize all the analog output priority arrays to NULL */
|
||||
for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) {
|
||||
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
|
||||
Analog_Output_Level[i][j] = AO_LEVEL_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Analog_Output_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_ANALOG_OUTPUTS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Analog_Output_Count(void)
|
||||
{
|
||||
return MAX_ANALOG_OUTPUTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Analog_Output_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Analog_Output_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_ANALOG_OUTPUTS;
|
||||
|
||||
if (object_instance < MAX_ANALOG_OUTPUTS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
float Analog_Output_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
float value = AO_RELINQUISH_DEFAULT;
|
||||
unsigned index = 0;
|
||||
unsigned i = 0;
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
|
||||
value = Analog_Output_Level[index][i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = 0; /* instance to index conversion */
|
||||
unsigned i = 0; /* loop counter */
|
||||
unsigned priority = 0; /* return value */
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) {
|
||||
priority = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
bool Analog_Output_Present_Value_Set(
|
||||
uint32_t object_instance, float value, unsigned priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */) && (value >= 0.0) &&
|
||||
(value <= 100.0)) {
|
||||
Analog_Output_Level[index][priority - 1] = (uint8_t)value;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Analog_Output_Present_Value_Relinquish(
|
||||
uint32_t object_instance, unsigned priority)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
index = Analog_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */)) {
|
||||
Analog_Output_Level[index][priority - 1] = AO_LEVEL_NULL;
|
||||
/* Note: you could set the physical output here to the next
|
||||
highest priority, or to the relinquish default if no
|
||||
priorities are set.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing output) */
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Analog_Output_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_ANALOG_OUTPUTS) {
|
||||
sprintf(
|
||||
text_string, "ANALOG OUTPUT %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Analog_Output_Out_Of_Service(uint32_t instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool oos_flag = false;
|
||||
|
||||
index = Analog_Output_Instance_To_Index(instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
oos_flag = Out_Of_Service[index];
|
||||
}
|
||||
|
||||
return oos_flag;
|
||||
}
|
||||
|
||||
void Analog_Output_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Analog_Output_Instance_To_Index(instance);
|
||||
if (index < MAX_ANALOG_OUTPUTS) {
|
||||
Out_Of_Service[index] = oos_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Analog_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
float real_value = (float)1.414;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_ANALOG_OUTPUT, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
Analog_Output_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_ANALOG_OUTPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
real_value = Analog_Output_Present_Value(rpdata->object_instance);
|
||||
apdu_len = encode_application_real(&apdu[0], real_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Analog_Output_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Analog_Output_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_UNITS:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT);
|
||||
break;
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
/* Array element zero is the number of elements in the array */
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
||||
/* if no index was specified, then try to encode the entire list
|
||||
*/
|
||||
/* into one packet. */
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
object_index =
|
||||
Analog_Output_Instance_To_Index(rpdata->object_instance);
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
/* FIXME: check if we have room before adding it to APDU */
|
||||
if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) {
|
||||
len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
real_value = Analog_Output_Level[object_index][i];
|
||||
len = encode_application_real(
|
||||
&apdu[apdu_len], real_value);
|
||||
}
|
||||
/* add it if we have room */
|
||||
if ((apdu_len + len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object_index =
|
||||
Analog_Output_Instance_To_Index(rpdata->object_instance);
|
||||
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
|
||||
if (Analog_Output_Level[object_index][rpdata->array_index -
|
||||
1] == AO_LEVEL_NULL) {
|
||||
apdu_len = encode_application_null(&apdu[0]);
|
||||
} else {
|
||||
real_value =
|
||||
Analog_Output_Level[object_index]
|
||||
[rpdata->array_index - 1];
|
||||
apdu_len =
|
||||
encode_application_real(&apdu[0], real_value);
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
real_value = AO_RELINQUISH_DEFAULT;
|
||||
apdu_len = encode_application_real(&apdu[0], real_value);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_REAL) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
status =
|
||||
Analog_Output_Present_Value_Set(wp_data->object_instance,
|
||||
value.type.Real, wp_data->priority);
|
||||
if (wp_data->priority == 6) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
status = Analog_Output_Present_Value_Relinquish(
|
||||
wp_data->object_instance, wp_data->priority);
|
||||
if (!status) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
Analog_Output_Out_Of_Service_Set(
|
||||
wp_data->object_instance, value.type.Boolean);
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_UNITS:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
/*
|
||||
* start out assuming success and only set up error
|
||||
* response if validation fails.
|
||||
*/
|
||||
bResult = true;
|
||||
if (pValue->tag != ucExpectedTag) {
|
||||
bResult = false;
|
||||
*pErrorClass = ERROR_CLASS_PROPERTY;
|
||||
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
|
||||
return (bResult);
|
||||
}
|
||||
|
||||
void testAnalogOutput(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Analog_Output_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_ANALOG_OUTPUT;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Analog_Output_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_ANALOG_OUTPUT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Analog Output", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testAnalogOutput);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_ANALOG_INPUT */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,139 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef AO_H
|
||||
#define AO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Analog_Output_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Analog_Output_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Output_Count(
|
||||
void);
|
||||
uint32_t Analog_Output_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Analog_Output_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Analog_Output_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
float Analog_Output_Present_Value(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Output_Present_Value_Priority(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Output_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value,
|
||||
unsigned priority);
|
||||
bool Analog_Output_Present_Value_Relinquish(
|
||||
uint32_t object_instance,
|
||||
unsigned priority);
|
||||
|
||||
float Analog_Output_Relinquish_Default(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Output_Relinquish_Default_Set(
|
||||
uint32_t object_instance,
|
||||
float value);
|
||||
|
||||
bool Analog_Output_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Analog_Output_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
bool Analog_Output_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
float Analog_Output_COV_Increment(
|
||||
uint32_t instance);
|
||||
void Analog_Output_COV_Increment_Set(
|
||||
uint32_t instance,
|
||||
float value);
|
||||
|
||||
bool Analog_Output_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Analog_Output_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Analog_Output_Description(
|
||||
uint32_t instance);
|
||||
bool Analog_Output_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_RELIABILITY Analog_Output_Reliability(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Output_Reliability_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_RELIABILITY value);
|
||||
|
||||
bool Analog_Output_Units_Set(
|
||||
uint32_t instance,
|
||||
uint16_t units);
|
||||
uint16_t Analog_Output_Units(
|
||||
uint32_t instance);
|
||||
|
||||
bool Analog_Output_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Analog_Output_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool oos_flag);
|
||||
|
||||
int Analog_Output_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Analog_Output_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Analog_Output_Create(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Output_Delete(
|
||||
uint32_t object_instance);
|
||||
void Analog_Output_Cleanup(
|
||||
void);
|
||||
void Analog_Output_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAnalogOutput(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_OUTPUT
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = ao.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = analog_output
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,180 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef AV_H
|
||||
#define AV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/rp.h"
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
#include "bacnet/basic/object/nc.h"
|
||||
#include "bacnet/alarm_ack.h"
|
||||
#include "bacnet/getevent.h"
|
||||
#include "bacnet/get_alarm_sum.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct analog_value_descr {
|
||||
unsigned Event_State:3;
|
||||
bool Out_Of_Service;
|
||||
uint16_t Units;
|
||||
float Present_Value;
|
||||
float Prior_Value;
|
||||
float COV_Increment;
|
||||
bool Changed;
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
uint32_t Time_Delay;
|
||||
uint32_t Notification_Class;
|
||||
float High_Limit;
|
||||
float Low_Limit;
|
||||
float Deadband;
|
||||
unsigned Limit_Enable:2;
|
||||
unsigned Event_Enable:3;
|
||||
unsigned Notify_Type:1;
|
||||
ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION];
|
||||
BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION];
|
||||
/* time to generate event notification */
|
||||
uint32_t Remaining_Time_Delay;
|
||||
/* AckNotification informations */
|
||||
ACK_NOTIFICATION Ack_notify_data;
|
||||
#endif
|
||||
} ANALOG_VALUE_DESCR;
|
||||
|
||||
|
||||
void Analog_Value_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Analog_Value_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Analog_Value_Count(
|
||||
void);
|
||||
uint32_t Analog_Value_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Analog_Value_Instance_To_Index(
|
||||
uint32_t object_instance);
|
||||
|
||||
bool Analog_Value_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Analog_Value_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
int Analog_Value_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
|
||||
bool Analog_Value_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Analog_Value_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
float value,
|
||||
uint8_t priority);
|
||||
float Analog_Value_Present_Value(
|
||||
uint32_t object_instance);
|
||||
|
||||
bool Analog_Value_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Analog_Value_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
bool Analog_Value_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
float Analog_Value_COV_Increment(
|
||||
uint32_t instance);
|
||||
void Analog_Value_COV_Increment_Set(
|
||||
uint32_t instance,
|
||||
float value);
|
||||
|
||||
char *Analog_Value_Description(
|
||||
uint32_t instance);
|
||||
bool Analog_Value_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_RELIABILITY Analog_Value_Reliability(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Value_Reliability_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_RELIABILITY value);
|
||||
|
||||
uint16_t Analog_Value_Units(
|
||||
uint32_t instance);
|
||||
bool Analog_Value_Units_Set(
|
||||
uint32_t instance,
|
||||
uint16_t unit);
|
||||
|
||||
bool Analog_Value_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Analog_Value_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool oos_flag);
|
||||
|
||||
/* note: header of Intrinsic_Reporting function is required
|
||||
even when INTRINSIC_REPORTING is not defined */
|
||||
void Analog_Value_Intrinsic_Reporting(
|
||||
uint32_t object_instance);
|
||||
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
int Analog_Value_Event_Information(
|
||||
unsigned index,
|
||||
BACNET_GET_EVENT_INFORMATION_DATA * getevent_data);
|
||||
|
||||
int Analog_Value_Alarm_Ack(
|
||||
BACNET_ALARM_ACK_DATA * alarmack_data,
|
||||
BACNET_ERROR_CODE * error_code);
|
||||
|
||||
int Analog_Value_Alarm_Summary(
|
||||
unsigned index,
|
||||
BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data);
|
||||
#endif
|
||||
|
||||
bool Analog_Value_Create(
|
||||
uint32_t object_instance);
|
||||
bool Analog_Value_Delete(
|
||||
uint32_t object_instance);
|
||||
void Analog_Value_Cleanup(
|
||||
void);
|
||||
void Analog_Value_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testAnalog_Value(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_ANALOG_VALUE
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = av.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = analog_value
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,603 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/awf.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/object/bacfile.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t instance;
|
||||
char *filename;
|
||||
} BACNET_FILE_LISTING;
|
||||
|
||||
#ifndef FILE_RECORD_SIZE
|
||||
#define FILE_RECORD_SIZE MAX_OCTET_STRING_BYTES
|
||||
#endif
|
||||
|
||||
static BACNET_FILE_LISTING BACnet_File_Listing[] = {
|
||||
{ 0, "temp_0.txt" }, { 1, "temp_1.txt" }, { 2, "temp_2.txt" },
|
||||
{ 0, NULL } /* last file indication */
|
||||
};
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int bacfile_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_FILE_TYPE, PROP_FILE_SIZE,
|
||||
PROP_MODIFICATION_DATE, PROP_ARCHIVE, PROP_READ_ONLY,
|
||||
PROP_FILE_ACCESS_METHOD, -1 };
|
||||
|
||||
static const int bacfile_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
|
||||
|
||||
static const int bacfile_Properties_Proprietary[] = { -1 };
|
||||
|
||||
void BACfile_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = bacfile_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = bacfile_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = bacfile_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static char *bacfile_name(uint32_t instance)
|
||||
{
|
||||
uint32_t index = 0;
|
||||
char *filename = NULL;
|
||||
|
||||
/* linear search for file instance match */
|
||||
while (BACnet_File_Listing[index].filename) {
|
||||
if (BACnet_File_Listing[index].instance == instance) {
|
||||
filename = BACnet_File_Listing[index].filename;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
bool bacfile_object_name(
|
||||
uint32_t instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
bool status = false;
|
||||
char *filename = NULL;
|
||||
|
||||
filename = bacfile_name(instance);
|
||||
if (filename) {
|
||||
status = characterstring_init_ansi(object_name, filename);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool bacfile_valid_instance(uint32_t object_instance)
|
||||
{
|
||||
return bacfile_name(object_instance) ? true : false;
|
||||
}
|
||||
|
||||
uint32_t bacfile_count(void)
|
||||
{
|
||||
uint32_t index = 0;
|
||||
|
||||
/* linear search for file instance match */
|
||||
while (BACnet_File_Listing[index].filename) {
|
||||
index++;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
uint32_t bacfile_index_to_instance(unsigned find_index)
|
||||
{
|
||||
uint32_t instance = BACNET_MAX_INSTANCE + 1;
|
||||
uint32_t index = 0;
|
||||
|
||||
/* bounds checking... */
|
||||
while (BACnet_File_Listing[index].filename) {
|
||||
if (index == find_index) {
|
||||
instance = BACnet_File_Listing[index].instance;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
static long fsize(FILE *pFile)
|
||||
{
|
||||
long size = 0;
|
||||
long origin = 0;
|
||||
|
||||
if (pFile) {
|
||||
origin = ftell(pFile);
|
||||
fseek(pFile, 0L, SEEK_END);
|
||||
size = ftell(pFile);
|
||||
fseek(pFile, origin, SEEK_SET);
|
||||
}
|
||||
return (size);
|
||||
}
|
||||
|
||||
unsigned bacfile_file_size(uint32_t object_instance)
|
||||
{
|
||||
char *pFilename = NULL;
|
||||
FILE *pFile = NULL;
|
||||
unsigned file_size = 0;
|
||||
|
||||
pFilename = bacfile_name(object_instance);
|
||||
if (pFilename) {
|
||||
pFile = fopen(pFilename, "rb");
|
||||
if (pFile) {
|
||||
file_size = fsize(pFile);
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
/* return the number of bytes used, or -1 on error */
|
||||
int bacfile_read_property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
char text_string[32] = { "" };
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_DATE bdate;
|
||||
BACNET_TIME btime;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_FILE, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
sprintf(text_string, "FILE %lu",
|
||||
(unsigned long)rpdata->object_instance);
|
||||
characterstring_init_ansi(&char_string, text_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_FILE);
|
||||
break;
|
||||
case PROP_DESCRIPTION:
|
||||
characterstring_init_ansi(
|
||||
&char_string, bacfile_name(rpdata->object_instance));
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_FILE_TYPE:
|
||||
characterstring_init_ansi(&char_string, "TEXT");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_FILE_SIZE:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], bacfile_file_size(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_MODIFICATION_DATE:
|
||||
/* FIXME: get the actual value instead of April Fool's Day */
|
||||
bdate.year = 2006; /* AD */
|
||||
bdate.month = 4; /* 1=Jan */
|
||||
bdate.day = 1; /* 1..31 */
|
||||
bdate.wday = 6; /* 1=Monday */
|
||||
apdu_len = encode_application_date(&apdu[0], &bdate);
|
||||
/* FIXME: get the actual value */
|
||||
btime.hour = 7;
|
||||
btime.min = 0;
|
||||
btime.sec = 3;
|
||||
btime.hundredths = 1;
|
||||
apdu_len += encode_application_time(&apdu[apdu_len], &btime);
|
||||
break;
|
||||
case PROP_ARCHIVE:
|
||||
/* 12.13.8 Archive
|
||||
This property, of type BOOLEAN, indicates whether the File
|
||||
object has been saved for historical or backup purposes. This
|
||||
property shall be logical TRUE only if no changes have been
|
||||
made to the file data by internal processes or through File
|
||||
Access Services since the last time the object was archived.
|
||||
*/
|
||||
/* FIXME: get the actual value: note it may be inverse... */
|
||||
apdu_len = encode_application_boolean(&apdu[0], true);
|
||||
break;
|
||||
case PROP_READ_ONLY:
|
||||
/* FIXME: get the actual value */
|
||||
apdu_len = encode_application_boolean(&apdu[0], true);
|
||||
break;
|
||||
case PROP_FILE_ACCESS_METHOD:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], FILE_RECORD_AND_STREAM_ACCESS);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
if (!bacfile_valid_instance(wp_data->object_instance)) {
|
||||
wp_data->error_class = ERROR_CLASS_OBJECT;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if (wp_data->array_index != BACNET_ARRAY_ALL) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_ARCHIVE:
|
||||
/* 12.13.8 Archive
|
||||
This property, of type BOOLEAN, indicates whether the File
|
||||
object has been saved for historical or backup purposes. This
|
||||
property shall be logical TRUE only if no changes have been
|
||||
made to the file data by internal processes or through File
|
||||
Access Services since the last time the object was archived. */
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
if (value.type.Boolean) {
|
||||
/* FIXME: do something to wp_data->object_instance */
|
||||
} else {
|
||||
/* FIXME: do something to wp_data->object_instance */
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_FILE_SIZE:
|
||||
/* If the file size can be changed by writing to the file,
|
||||
and File_Access_Method is STREAM_ACCESS, then this property
|
||||
shall be writable. */
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
/* FIXME: do something with value.type.Unsigned
|
||||
to wp_data->object_instance */
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_FILE_TYPE:
|
||||
case PROP_MODIFICATION_DATE:
|
||||
case PROP_READ_ONLY:
|
||||
case PROP_FILE_ACCESS_METHOD:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
uint32_t bacfile_instance(char *filename)
|
||||
{
|
||||
uint32_t index = 0;
|
||||
uint32_t instance = BACNET_MAX_INSTANCE + 1;
|
||||
|
||||
/* linear search for filename match */
|
||||
while (BACnet_File_Listing[index].filename) {
|
||||
if (strcmp(BACnet_File_Listing[index].filename, filename) == 0) {
|
||||
instance = BACnet_File_Listing[index].instance;
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
#if MAX_TSM_TRANSACTIONS
|
||||
/* this is one way to match up the invoke ID with */
|
||||
/* the file ID from the AtomicReadFile request. */
|
||||
/* Another way would be to store the */
|
||||
/* invokeID and file instance in a list or table */
|
||||
/* when the request was sent */
|
||||
uint32_t bacfile_instance_from_tsm(uint8_t invokeID)
|
||||
{
|
||||
BACNET_NPDU_DATA npdu_data = { 0 }; /* dummy for getting npdu length */
|
||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||
uint8_t service_choice = 0;
|
||||
uint8_t *service_request = NULL;
|
||||
uint16_t service_request_len = 0;
|
||||
BACNET_ADDRESS dest; /* where the original packet was destined */
|
||||
uint8_t apdu[MAX_PDU] = { 0 }; /* original APDU packet */
|
||||
uint16_t apdu_len = 0; /* original APDU packet length */
|
||||
int len = 0; /* apdu header length */
|
||||
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
|
||||
uint32_t object_instance = BACNET_MAX_INSTANCE + 1; /* return value */
|
||||
bool found = false;
|
||||
|
||||
found = tsm_get_transaction_pdu(
|
||||
invokeID, &dest, &npdu_data, &apdu[0], &apdu_len);
|
||||
if (found) {
|
||||
if (!npdu_data.network_layer_message &&
|
||||
npdu_data.data_expecting_reply &&
|
||||
(apdu[0] == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
|
||||
len = apdu_decode_confirmed_service_request(&apdu[0], apdu_len,
|
||||
&service_data, &service_choice, &service_request,
|
||||
&service_request_len);
|
||||
if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) {
|
||||
len = arf_decode_service_request(
|
||||
service_request, service_request_len, &data);
|
||||
if (len > 0) {
|
||||
if (data.object_type == OBJECT_FILE) {
|
||||
object_instance = data.object_instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return object_instance;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bacfile_read_stream_data(BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
char *pFilename = NULL;
|
||||
bool found = false;
|
||||
FILE *pFile = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
pFilename = bacfile_name(data->object_instance);
|
||||
if (pFilename) {
|
||||
found = true;
|
||||
pFile = fopen(pFilename, "rb");
|
||||
if (pFile) {
|
||||
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
|
||||
len = fread(octetstring_value(&data->fileData[0]), 1,
|
||||
data->type.stream.requestedOctetCount, pFile);
|
||||
if (len < data->type.stream.requestedOctetCount) {
|
||||
data->endOfFile = true;
|
||||
} else {
|
||||
data->endOfFile = false;
|
||||
}
|
||||
octetstring_truncate(&data->fileData[0], len);
|
||||
fclose(pFile);
|
||||
} else {
|
||||
octetstring_truncate(&data->fileData[0], 0);
|
||||
data->endOfFile = true;
|
||||
}
|
||||
} else {
|
||||
octetstring_truncate(&data->fileData[0], 0);
|
||||
data->endOfFile = true;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool bacfile_write_stream_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
char *pFilename = NULL;
|
||||
bool found = false;
|
||||
FILE *pFile = NULL;
|
||||
|
||||
pFilename = bacfile_name(data->object_instance);
|
||||
if (pFilename) {
|
||||
found = true;
|
||||
if (data->type.stream.fileStartPosition == 0) {
|
||||
/* open the file as a clean slate when starting at 0 */
|
||||
pFile = fopen(pFilename, "wb");
|
||||
} else if (data->type.stream.fileStartPosition == -1) {
|
||||
/* If 'File Start Position' parameter has the special
|
||||
value -1, then the write operation shall be treated
|
||||
as an append to the current end of file. */
|
||||
pFile = fopen(pFilename, "ab+");
|
||||
} else {
|
||||
/* open for update */
|
||||
pFile = fopen(pFilename, "rb+");
|
||||
}
|
||||
if (pFile) {
|
||||
if (data->type.stream.fileStartPosition != -1) {
|
||||
(void)fseek(
|
||||
pFile, data->type.stream.fileStartPosition, SEEK_SET);
|
||||
}
|
||||
if (fwrite(octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
|
||||
/* do something if it fails? */
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool bacfile_write_record_data(BACNET_ATOMIC_WRITE_FILE_DATA *data)
|
||||
{
|
||||
char *pFilename = NULL;
|
||||
bool found = false;
|
||||
FILE *pFile = NULL;
|
||||
uint32_t i = 0;
|
||||
char dummy_data[FILE_RECORD_SIZE];
|
||||
char *pData = NULL;
|
||||
|
||||
pFilename = bacfile_name(data->object_instance);
|
||||
if (pFilename) {
|
||||
found = true;
|
||||
if (data->type.record.fileStartRecord == 0) {
|
||||
/* open the file as a clean slate when starting at 0 */
|
||||
pFile = fopen(pFilename, "wb");
|
||||
} else if (data->type.record.fileStartRecord == -1) {
|
||||
/* If 'File Start Record' parameter has the special
|
||||
value -1, then the write operation shall be treated
|
||||
as an append to the current end of file. */
|
||||
pFile = fopen(pFilename, "ab+");
|
||||
} else {
|
||||
/* open for update */
|
||||
pFile = fopen(pFilename, "rb+");
|
||||
}
|
||||
if (pFile) {
|
||||
if ((data->type.record.fileStartRecord != -1) &&
|
||||
(data->type.record.fileStartRecord > 0)) {
|
||||
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
|
||||
i++) {
|
||||
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
|
||||
if ((pData == NULL) || feof(pFile)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < data->type.record.returnedRecordCount; i++) {
|
||||
if (fwrite(octetstring_value(&data->fileData[i]),
|
||||
octetstring_length(&data->fileData[i]), 1,
|
||||
pFile) != 1) {
|
||||
/* do something if it fails? */
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool bacfile_read_ack_stream_data(
|
||||
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
bool found = false;
|
||||
FILE *pFile = NULL;
|
||||
char *pFilename = NULL;
|
||||
|
||||
pFilename = bacfile_name(instance);
|
||||
if (pFilename) {
|
||||
found = true;
|
||||
pFile = fopen(pFilename, "rb");
|
||||
if (pFile) {
|
||||
(void)fseek(pFile, data->type.stream.fileStartPosition, SEEK_SET);
|
||||
if (fwrite(octetstring_value(&data->fileData[0]),
|
||||
octetstring_length(&data->fileData[0]), 1, pFile) != 1) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
|
||||
(unsigned long)instance);
|
||||
#endif
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool bacfile_read_ack_record_data(
|
||||
uint32_t instance, BACNET_ATOMIC_READ_FILE_DATA *data)
|
||||
{
|
||||
bool found = false;
|
||||
FILE *pFile = NULL;
|
||||
char *pFilename = NULL;
|
||||
uint32_t i = 0;
|
||||
char dummy_data[MAX_OCTET_STRING_BYTES] = { 0 };
|
||||
char *pData = NULL;
|
||||
|
||||
pFilename = bacfile_name(instance);
|
||||
if (pFilename) {
|
||||
found = true;
|
||||
pFile = fopen(pFilename, "rb");
|
||||
if (pFile) {
|
||||
if (data->type.record.fileStartRecord > 0) {
|
||||
for (i = 0; i < (uint32_t)data->type.record.fileStartRecord;
|
||||
i++) {
|
||||
pData = fgets(&dummy_data[0], sizeof(dummy_data), pFile);
|
||||
if ((pData == NULL) || feof(pFile)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = 0; i < data->type.record.RecordCount; i++) {
|
||||
if (fwrite(octetstring_value(&data->fileData[i]),
|
||||
octetstring_length(&data->fileData[i]), 1,
|
||||
pFile) != 1) {
|
||||
#if PRINT_ENABLED
|
||||
fprintf(stderr, "Failed to write to %s (%lu)!\n", pFilename,
|
||||
(unsigned long)instance);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void bacfile_init(void)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
#ifndef BACFILE_H
|
||||
#define BACFILE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/arf.h"
|
||||
#include "bacnet/awf.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void BACfile_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool bacfile_object_name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool bacfile_valid_instance(
|
||||
uint32_t object_instance);
|
||||
uint32_t bacfile_count(
|
||||
void);
|
||||
uint32_t bacfile_index_to_instance(
|
||||
unsigned find_index);
|
||||
unsigned bacfile_instance_to_index(
|
||||
uint32_t instance);
|
||||
uint32_t bacfile_instance(
|
||||
char *filename);
|
||||
/* this is one way to match up the invoke ID with */
|
||||
/* the file ID from the AtomicReadFile request. */
|
||||
/* Another way would be to store the */
|
||||
/* invokeID and file instance in a list or table */
|
||||
/* when the request was sent */
|
||||
uint32_t bacfile_instance_from_tsm(
|
||||
uint8_t invokeID);
|
||||
|
||||
/* handler ACK helper */
|
||||
bool bacfile_read_stream_data(
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
bool bacfile_read_ack_stream_data(
|
||||
uint32_t instance,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
bool bacfile_write_stream_data(
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
bool bacfile_read_record_data(
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
bool bacfile_read_ack_record_data(
|
||||
uint32_t instance,
|
||||
BACNET_ATOMIC_READ_FILE_DATA * data);
|
||||
bool bacfile_write_record_data(
|
||||
BACNET_ATOMIC_WRITE_FILE_DATA * data);
|
||||
|
||||
void bacfile_init(
|
||||
void);
|
||||
uint32_t bacfile_file_size(
|
||||
uint32_t instance);
|
||||
|
||||
/* handling for read property service */
|
||||
int bacfile_read_property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
|
||||
/* handling for write property service */
|
||||
bool bacfile_write_property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,552 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Input Objects customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/cov.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/basic/object/bi.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#ifndef MAX_BINARY_INPUTS
|
||||
#define MAX_BINARY_INPUTS 5
|
||||
#endif
|
||||
|
||||
/* stores the current value */
|
||||
static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS];
|
||||
/* out of service decouples physical input from Present_Value */
|
||||
static bool Out_Of_Service[MAX_BINARY_INPUTS];
|
||||
/* Change of Value flag */
|
||||
static bool Change_Of_Value[MAX_BINARY_INPUTS];
|
||||
/* Polarity of Input */
|
||||
static BACNET_POLARITY Polarity[MAX_BINARY_INPUTS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Binary_Input_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
||||
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, -1 };
|
||||
|
||||
static const int Binary_Input_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
|
||||
|
||||
static const int Binary_Input_Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Binary_Input_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Binary_Input_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Binary_Input_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Binary_Input_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Binary_Input_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_INPUTS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Binary_Input_Count(void)
|
||||
{
|
||||
return MAX_BINARY_INPUTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Binary_Input_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
void Binary_Input_Init(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
unsigned i;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
/* initialize all the values */
|
||||
for (i = 0; i < MAX_BINARY_INPUTS; i++) {
|
||||
Present_Value[i] = BINARY_INACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Binary_Input_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_INPUTS;
|
||||
|
||||
if (object_instance < MAX_BINARY_INPUTS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = BINARY_INACTIVE;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
value = Present_Value[index];
|
||||
if (Polarity[index] != POLARITY_NORMAL) {
|
||||
if (value == BINARY_INACTIVE) {
|
||||
value = BINARY_ACTIVE;
|
||||
} else {
|
||||
value = BINARY_INACTIVE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool Binary_Input_Out_Of_Service(uint32_t object_instance)
|
||||
{
|
||||
bool value = false;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
value = Out_Of_Service[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool Binary_Input_Change_Of_Value(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned index;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
status = Change_Of_Value[index];
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void Binary_Input_Change_Of_Value_Clear(uint32_t object_instance)
|
||||
{
|
||||
unsigned index;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
Change_Of_Value[index] = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, loads the value_list with the COV data.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value_list - list of COV data
|
||||
*
|
||||
* @return true if the value list is encoded
|
||||
*/
|
||||
bool Binary_Input_Encode_Value_List(
|
||||
uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
if (value_list) {
|
||||
value_list->propertyIdentifier = PROP_PRESENT_VALUE;
|
||||
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
value_list->value.context_specific = false;
|
||||
value_list->value.tag = BACNET_APPLICATION_TAG_ENUMERATED;
|
||||
value_list->value.next = NULL;
|
||||
value_list->value.type.Enumerated =
|
||||
Binary_Input_Present_Value(object_instance);
|
||||
value_list->priority = BACNET_NO_PRIORITY;
|
||||
value_list = value_list->next;
|
||||
}
|
||||
if (value_list) {
|
||||
value_list->propertyIdentifier = PROP_STATUS_FLAGS;
|
||||
value_list->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
value_list->value.context_specific = false;
|
||||
value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING;
|
||||
value_list->value.next = NULL;
|
||||
bitstring_init(&value_list->value.type.Bit_String);
|
||||
bitstring_set_bit(
|
||||
&value_list->value.type.Bit_String, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(
|
||||
&value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(
|
||||
&value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false);
|
||||
if (Binary_Input_Out_Of_Service(object_instance)) {
|
||||
bitstring_set_bit(&value_list->value.type.Bit_String,
|
||||
STATUS_FLAG_OUT_OF_SERVICE, true);
|
||||
} else {
|
||||
bitstring_set_bit(&value_list->value.type.Bit_String,
|
||||
STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
}
|
||||
value_list->priority = BACNET_NO_PRIORITY;
|
||||
value_list->next = NULL;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Binary_Input_Present_Value_Set(
|
||||
uint32_t object_instance, BACNET_BINARY_PV value)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool status = false;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
if (Polarity[index] != POLARITY_NORMAL) {
|
||||
if (value == BINARY_INACTIVE) {
|
||||
value = BINARY_ACTIVE;
|
||||
} else {
|
||||
value = BINARY_INACTIVE;
|
||||
}
|
||||
}
|
||||
if (Present_Value[index] != value) {
|
||||
Change_Of_Value[index] = true;
|
||||
}
|
||||
Present_Value[index] = value;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void Binary_Input_Out_Of_Service_Set(uint32_t object_instance, bool value)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
if (Out_Of_Service[index] != value) {
|
||||
Change_Of_Value[index] = true;
|
||||
}
|
||||
Out_Of_Service[index] = value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
bool Binary_Input_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
sprintf(
|
||||
text_string, "BINARY INPUT %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
BACNET_POLARITY Binary_Input_Polarity(uint32_t object_instance)
|
||||
{
|
||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
polarity = Polarity[index];
|
||||
}
|
||||
|
||||
return polarity;
|
||||
}
|
||||
|
||||
bool Binary_Input_Polarity_Set(
|
||||
uint32_t object_instance, BACNET_POLARITY polarity)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Input_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_INPUTS) {
|
||||
Polarity[index] = polarity;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return apdu length, or BACNET_STATUS_ERROR on error */
|
||||
/* assumption - object already exists, and has been bounds checked */
|
||||
int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
uint8_t *apdu = NULL;
|
||||
bool state = false;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_BINARY_INPUT, rpdata->object_instance);
|
||||
break;
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
/* note: object name must be unique in our device */
|
||||
Binary_Input_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_INPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
/* note: you need to look up the actual value */
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], Binary_Input_Present_Value(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
/* note: see the details in the standard on how to use these */
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Binary_Input_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
/* note: see the details in the standard on how to use this */
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Binary_Input_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[0], Binary_Input_Polarity(rpdata->object_instance));
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if (wp_data->array_index != BACNET_ARRAY_ALL) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
if (value.type.Enumerated <= MAX_BINARY_PV) {
|
||||
Binary_Input_Present_Value_Set(wp_data->object_instance,
|
||||
(BACNET_BINARY_PV)value.type.Enumerated);
|
||||
} else {
|
||||
status = false;
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
Binary_Input_Out_Of_Service_Set(
|
||||
wp_data->object_instance, value.type.Boolean);
|
||||
}
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
if (value.type.Enumerated < MAX_POLARITY) {
|
||||
Binary_Input_Polarity_Set(wp_data->object_instance,
|
||||
(BACNET_POLARITY)value.type.Enumerated);
|
||||
} else {
|
||||
status = false;
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testBinaryInput(Test *pTest)
|
||||
{
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
|
||||
Binary_Input_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_BINARY_INPUT;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Binary_Input_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_INPUT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary Input", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinaryInput);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_INPUT */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,135 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BI_H
|
||||
#define BI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/cov.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Binary_Input_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Binary_Input_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Binary_Input_Count(
|
||||
void);
|
||||
uint32_t Binary_Input_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Binary_Input_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Binary_Input_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Binary_Input_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Binary_Input_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_BINARY_PV Binary_Input_Present_Value(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Input_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_BINARY_PV value);
|
||||
|
||||
char *Binary_Input_Description(
|
||||
uint32_t instance);
|
||||
bool Binary_Input_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_RELIABILITY Binary_Input_Reliability(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Input_Reliability_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_RELIABILITY value);
|
||||
|
||||
char *Binary_Input_Inactive_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Input_Inactive_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
char *Binary_Input_Active_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Input_Active_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_POLARITY Binary_Input_Polarity(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Input_Polarity_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_POLARITY polarity);
|
||||
|
||||
bool Binary_Input_Out_Of_Service(
|
||||
uint32_t object_instance);
|
||||
void Binary_Input_Out_Of_Service_Set(
|
||||
uint32_t object_instance,
|
||||
bool value);
|
||||
|
||||
bool Binary_Input_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
bool Binary_Input_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Binary_Input_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
|
||||
int Binary_Input_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
|
||||
bool Binary_Input_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Binary_Input_Create(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Input_Delete(
|
||||
uint32_t object_instance);
|
||||
void Binary_Input_Cleanup(
|
||||
void);
|
||||
void Binary_Input_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBinaryInput(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_BINARY_INPUT
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = bi.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = binary_input
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,519 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Output Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/bo.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#ifndef MAX_BINARY_OUTPUTS
|
||||
#define MAX_BINARY_OUTPUTS 4
|
||||
#endif
|
||||
|
||||
/* When all the priorities are level null, the present value returns */
|
||||
/* the Relinquish Default value */
|
||||
#define RELINQUISH_DEFAULT BINARY_INACTIVE
|
||||
/* Here is our Priority Array.*/
|
||||
static BACNET_BINARY_PV Binary_Output_Level[MAX_BINARY_OUTPUTS]
|
||||
[BACNET_MAX_PRIORITY];
|
||||
/* Writable out-of-service allows others to play with our Present Value */
|
||||
/* without changing the physical output */
|
||||
static bool Out_Of_Service[MAX_BINARY_OUTPUTS];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Binary_Output_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
||||
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_POLARITY, PROP_PRIORITY_ARRAY,
|
||||
PROP_RELINQUISH_DEFAULT, -1 };
|
||||
|
||||
static const int Binary_Output_Properties_Optional[] = { PROP_DESCRIPTION,
|
||||
PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 };
|
||||
|
||||
static const int Binary_Output_Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Binary_Output_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Binary_Output_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Binary_Output_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Binary_Output_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Binary_Output_Init(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
/* initialize all the analog output priority arrays to NULL */
|
||||
for (i = 0; i < MAX_BINARY_OUTPUTS; i++) {
|
||||
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
|
||||
Binary_Output_Level[i][j] = BINARY_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Binary_Output_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_OUTPUTS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Binary_Output_Count(void)
|
||||
{
|
||||
return MAX_BINARY_OUTPUTS;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Binary_Output_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Binary_Output_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_OUTPUTS;
|
||||
|
||||
if (object_instance < MAX_BINARY_OUTPUTS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
|
||||
unsigned index = 0;
|
||||
unsigned i = 0;
|
||||
|
||||
index = Binary_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_OUTPUTS) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (Binary_Output_Level[index][i] != BINARY_NULL) {
|
||||
value = Binary_Output_Level[index][i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool Binary_Output_Out_Of_Service(uint32_t object_instance)
|
||||
{
|
||||
bool value = false;
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Output_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_OUTPUTS) {
|
||||
value = Out_Of_Service[index];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Binary_Output_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_BINARY_OUTPUTS) {
|
||||
sprintf(
|
||||
text_string, "BINARY OUTPUT %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Binary_Output_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
BACNET_POLARITY polarity = POLARITY_NORMAL;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_BINARY_OUTPUT, rpdata->object_instance);
|
||||
break;
|
||||
/* note: Name and Description don't have to be the same.
|
||||
You could make Description writable and different */
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
Binary_Output_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_OUTPUT);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value =
|
||||
Binary_Output_Present_Value(rpdata->object_instance);
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
/* note: see the details in the standard on how to use these */
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
/* note: see the details in the standard on how to use this */
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
object_index =
|
||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
||||
state = Out_Of_Service[object_index];
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_POLARITY:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], polarity);
|
||||
break;
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
/* Array element zero is the number of elements in the array */
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
||||
/* if no index was specified, then try to encode the entire list
|
||||
*/
|
||||
/* into one packet. */
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
object_index =
|
||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
/* FIXME: check if we have room before adding it to APDU */
|
||||
if (Binary_Output_Level[object_index][i] == BINARY_NULL) {
|
||||
len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
present_value = Binary_Output_Level[object_index][i];
|
||||
len = encode_application_enumerated(
|
||||
&apdu[apdu_len], present_value);
|
||||
}
|
||||
/* add it if we have room */
|
||||
if ((apdu_len + len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object_index =
|
||||
Binary_Output_Instance_To_Index(rpdata->object_instance);
|
||||
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
|
||||
if (Binary_Output_Level[object_index][rpdata->array_index -
|
||||
1] == BINARY_NULL) {
|
||||
apdu_len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
present_value =
|
||||
Binary_Output_Level[object_index]
|
||||
[rpdata->array_index - 1];
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[apdu_len], present_value);
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
present_value = RELINQUISH_DEFAULT;
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
case PROP_ACTIVE_TEXT:
|
||||
characterstring_init_ansi(&char_string, "on");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_INACTIVE_TEXT:
|
||||
characterstring_init_ansi(&char_string, "off");
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
unsigned int priority = 0;
|
||||
BACNET_BINARY_PV level = BINARY_NULL;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
/* only array properties can have array options */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
priority = wp_data->priority;
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */) &&
|
||||
(value.type.Enumerated <= MAX_BINARY_PV)) {
|
||||
level = (BACNET_BINARY_PV)value.type.Enumerated;
|
||||
object_index = Binary_Output_Instance_To_Index(
|
||||
wp_data->object_instance);
|
||||
priority--;
|
||||
Binary_Output_Level[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here if we
|
||||
are the highest priority.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing
|
||||
output) */
|
||||
status = true;
|
||||
} else if (priority == 6) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
level = BINARY_NULL;
|
||||
object_index = Binary_Output_Instance_To_Index(
|
||||
wp_data->object_instance);
|
||||
priority = wp_data->priority;
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
||||
priority--;
|
||||
Binary_Output_Level[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here to the
|
||||
next highest priority, or to the relinquish default
|
||||
if no priorities are set. However, if Out of Service
|
||||
is TRUE, then don't set the physical output. This
|
||||
comment may apply to the
|
||||
main loop (i.e. check out of service before changing
|
||||
output) */
|
||||
} else {
|
||||
status = false;
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
object_index =
|
||||
Binary_Output_Instance_To_Index(wp_data->object_instance);
|
||||
Out_Of_Service[object_index] = value.type.Boolean;
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_RELIABILITY:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_POLARITY:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
case PROP_ACTIVE_TEXT:
|
||||
case PROP_INACTIVE_TEXT:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testBinaryOutput(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Binary_Output_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_BINARY_OUTPUT;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Binary_Output_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_OUTPUT
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary Output", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinaryOutput);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_INPUT */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,141 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BO_H
|
||||
#define BO_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Binary_Output_Init(
|
||||
void);
|
||||
|
||||
void Binary_Output_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Binary_Output_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Binary_Output_Count(
|
||||
void);
|
||||
uint32_t Binary_Output_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Binary_Output_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Binary_Output_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Binary_Output_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Binary_Output_Description(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
char *Binary_Output_Inactive_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Inactive_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
char *Binary_Output_Active_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Active_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_BINARY_PV Binary_Output_Present_Value(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Present_Value_Set(
|
||||
uint32_t instance,
|
||||
BACNET_BINARY_PV binary_value,
|
||||
unsigned priority);
|
||||
bool Binary_Output_Present_Value_Relinquish(
|
||||
uint32_t instance,
|
||||
unsigned priority);
|
||||
unsigned Binary_Output_Present_Value_Priority(
|
||||
uint32_t object_instance);
|
||||
|
||||
BACNET_POLARITY Binary_Output_Polarity(
|
||||
uint32_t instance);
|
||||
bool Binary_Output_Polarity_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_POLARITY polarity);
|
||||
|
||||
bool Binary_Output_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Binary_Output_Out_Of_Service_Set(
|
||||
uint32_t object_instance,
|
||||
bool value);
|
||||
|
||||
BACNET_BINARY_PV Binary_Output_Relinquish_Default(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Output_Relinquish_Default_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_BINARY_PV value);
|
||||
|
||||
bool Binary_Output_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
bool Binary_Output_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Binary_Output_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
|
||||
int Binary_Output_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Binary_Output_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Binary_Output_Create(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Output_Delete(
|
||||
uint32_t object_instance);
|
||||
void Binary_Output_Cleanup(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBinaryOutput(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_OUTPUT
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = bo.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = binary_output
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
@@ -0,0 +1,506 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
/* Binary Output Objects - customize for your use */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/basic/object/bv.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
|
||||
#ifndef MAX_BINARY_VALUES
|
||||
#define MAX_BINARY_VALUES 10
|
||||
#endif
|
||||
|
||||
/* When all the priorities are level null, the present value returns */
|
||||
/* the Relinquish Default value */
|
||||
#define RELINQUISH_DEFAULT BINARY_INACTIVE
|
||||
/* Here is our Priority Array.*/
|
||||
static BACNET_BINARY_PV Binary_Value_Level[MAX_BINARY_VALUES]
|
||||
[BACNET_MAX_PRIORITY];
|
||||
/* Writable out-of-service allows others to play with our Present Value */
|
||||
/* without changing the physical output */
|
||||
static bool Out_Of_Service[MAX_BINARY_VALUES];
|
||||
|
||||
/* These three arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Binary_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS,
|
||||
PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, -1 };
|
||||
|
||||
static const int Binary_Value_Properties_Optional[] = { PROP_DESCRIPTION,
|
||||
PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, -1 };
|
||||
|
||||
static const int Binary_Value_Properties_Proprietary[] = { -1 };
|
||||
|
||||
void Binary_Value_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Binary_Value_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Binary_Value_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Binary_Value_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Binary_Value_Init(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
static bool initialized = false;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
/* initialize all the analog output priority arrays to NULL */
|
||||
for (i = 0; i < MAX_BINARY_VALUES; i++) {
|
||||
for (j = 0; j < BACNET_MAX_PRIORITY; j++) {
|
||||
Binary_Value_Level[i][j] = BINARY_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need validate that the */
|
||||
/* given instance exists */
|
||||
bool Binary_Value_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then count how many you have */
|
||||
unsigned Binary_Value_Count(void)
|
||||
{
|
||||
return MAX_BINARY_VALUES;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the instance */
|
||||
/* that correlates to the correct index */
|
||||
uint32_t Binary_Value_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/* we simply have 0-n object instances. Yours might be */
|
||||
/* more complex, and then you need to return the index */
|
||||
/* that correlates to the correct instance number */
|
||||
unsigned Binary_Value_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_BINARY_VALUES;
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
BACNET_BINARY_PV value = RELINQUISH_DEFAULT;
|
||||
unsigned index = 0;
|
||||
unsigned i = 0;
|
||||
|
||||
index = Binary_Value_Instance_To_Index(object_instance);
|
||||
if (index < MAX_BINARY_VALUES) {
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
if (Binary_Value_Level[index][i] != BINARY_NULL) {
|
||||
value = Binary_Value_Level[index][i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/* note: the object name must be unique within this device */
|
||||
bool Binary_Value_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
bool status = false;
|
||||
|
||||
if (object_instance < MAX_BINARY_VALUES) {
|
||||
sprintf(
|
||||
text_string, "BINARY VALUE %lu", (unsigned long)object_instance);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Binary_Value_Out_Of_Service(uint32_t instance)
|
||||
{
|
||||
unsigned index = 0;
|
||||
bool oos_flag = false;
|
||||
|
||||
index = Binary_Value_Instance_To_Index(instance);
|
||||
if (index < MAX_BINARY_VALUES) {
|
||||
oos_flag = Out_Of_Service[index];
|
||||
}
|
||||
|
||||
return oos_flag;
|
||||
}
|
||||
|
||||
void Binary_Value_Out_Of_Service_Set(uint32_t instance, bool oos_flag)
|
||||
{
|
||||
unsigned index = 0;
|
||||
|
||||
index = Binary_Value_Instance_To_Index(instance);
|
||||
if (index < MAX_BINARY_VALUES) {
|
||||
Out_Of_Service[index] = oos_flag;
|
||||
}
|
||||
}
|
||||
|
||||
/* return apdu len, or BACNET_STATUS_ERROR on error */
|
||||
int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0; /* return value */
|
||||
BACNET_BIT_STRING bit_string;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
BACNET_BINARY_PV present_value = BINARY_INACTIVE;
|
||||
unsigned object_index = 0;
|
||||
unsigned i = 0;
|
||||
bool state = false;
|
||||
uint8_t *apdu = NULL;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu = rpdata->application_data;
|
||||
switch (rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_BINARY_VALUE, rpdata->object_instance);
|
||||
break;
|
||||
/* note: Name and Description don't have to be the same.
|
||||
You could make Description writable and different */
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
Binary_Value_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], OBJECT_BINARY_VALUE);
|
||||
break;
|
||||
case PROP_PRESENT_VALUE:
|
||||
present_value = Binary_Value_Present_Value(rpdata->object_instance);
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
case PROP_STATUS_FLAGS:
|
||||
/* note: see the details in the standard on how to use these */
|
||||
bitstring_init(&bit_string);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
|
||||
state = Binary_Value_Out_Of_Service(rpdata->object_instance);
|
||||
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, state);
|
||||
apdu_len = encode_application_bitstring(&apdu[0], &bit_string);
|
||||
break;
|
||||
case PROP_EVENT_STATE:
|
||||
/* note: see the details in the standard on how to use this */
|
||||
apdu_len =
|
||||
encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL);
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
state = Binary_Value_Out_Of_Service(rpdata->object_instance);
|
||||
apdu_len = encode_application_boolean(&apdu[0], state);
|
||||
break;
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
/* Array element zero is the number of elements in the array */
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], BACNET_MAX_PRIORITY);
|
||||
/* if no index was specified, then try to encode the entire list
|
||||
*/
|
||||
/* into one packet. */
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(rpdata->object_instance);
|
||||
for (i = 0; i < BACNET_MAX_PRIORITY; i++) {
|
||||
/* FIXME: check if we have room before adding it to APDU */
|
||||
if (Binary_Value_Level[object_index][i] == BINARY_NULL) {
|
||||
len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
present_value = Binary_Value_Level[object_index][i];
|
||||
len = encode_application_enumerated(
|
||||
&apdu[apdu_len], present_value);
|
||||
}
|
||||
/* add it if we have room */
|
||||
if ((apdu_len + len) < MAX_APDU) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
object_index =
|
||||
Binary_Value_Instance_To_Index(rpdata->object_instance);
|
||||
if (rpdata->array_index <= BACNET_MAX_PRIORITY) {
|
||||
if (Binary_Value_Level[object_index][rpdata->array_index] ==
|
||||
BINARY_NULL) {
|
||||
apdu_len = encode_application_null(&apdu[apdu_len]);
|
||||
} else {
|
||||
present_value = Binary_Value_Level[object_index]
|
||||
[rpdata->array_index];
|
||||
apdu_len = encode_application_enumerated(
|
||||
&apdu[apdu_len], present_value);
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
present_value = RELINQUISH_DEFAULT;
|
||||
apdu_len = encode_application_enumerated(&apdu[0], present_value);
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* returns true if successful */
|
||||
bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
unsigned int priority = 0;
|
||||
BACNET_BINARY_PV level = BINARY_NULL;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_PRIORITY_ARRAY) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
switch (wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) {
|
||||
priority = wp_data->priority;
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY) &&
|
||||
(priority != 6 /* reserved */) &&
|
||||
(value.type.Enumerated <= MAX_BINARY_PV)) {
|
||||
level = (BACNET_BINARY_PV)value.type.Enumerated;
|
||||
object_index = Binary_Value_Instance_To_Index(
|
||||
wp_data->object_instance);
|
||||
priority--;
|
||||
Binary_Value_Level[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here if we
|
||||
are the highest priority.
|
||||
However, if Out of Service is TRUE, then don't set the
|
||||
physical output. This comment may apply to the
|
||||
main loop (i.e. check out of service before changing
|
||||
output) */
|
||||
status = true;
|
||||
} else if (priority == 6) {
|
||||
/* Command priority 6 is reserved for use by Minimum On/Off
|
||||
algorithm and may not be used for other purposes in any
|
||||
object. */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
} else {
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_NULL,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
level = BINARY_NULL;
|
||||
object_index = Binary_Value_Instance_To_Index(
|
||||
wp_data->object_instance);
|
||||
priority = wp_data->priority;
|
||||
if (priority && (priority <= BACNET_MAX_PRIORITY)) {
|
||||
priority--;
|
||||
Binary_Value_Level[object_index][priority] = level;
|
||||
/* Note: you could set the physical output here to the
|
||||
next highest priority, or to the relinquish default
|
||||
if no priorities are set. However, if Out of Service
|
||||
is TRUE, then don't set the physical output. This
|
||||
comment may apply to the
|
||||
main loop (i.e. check out of service before changing
|
||||
output) */
|
||||
} else {
|
||||
status = false;
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROP_OUT_OF_SERVICE:
|
||||
status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
Binary_Value_Out_Of_Service_Set(
|
||||
wp_data->object_instance, value.type.Boolean);
|
||||
}
|
||||
break;
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_STATUS_FLAGS:
|
||||
case PROP_EVENT_STATE:
|
||||
case PROP_PRIORITY_ARRAY:
|
||||
case PROP_RELINQUISH_DEFAULT:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
pValue = pValue;
|
||||
ucExpectedTag = ucExpectedTag;
|
||||
pErrorClass = pErrorClass;
|
||||
pErrorCode = pErrorCode;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void testBinary_Value(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
|
||||
Binary_Value_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_BINARY_VALUE;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Binary_Value_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_BINARY_VALUE
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Binary_Value", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testBinary_Value);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_BINARY_VALUE */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,152 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2006 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
#ifndef BV_H
|
||||
#define BV_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
void Binary_Value_Init(
|
||||
void);
|
||||
|
||||
void Binary_Value_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Binary_Value_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Binary_Value_Count(
|
||||
void);
|
||||
uint32_t Binary_Value_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Binary_Value_Instance_To_Index(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Value_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Binary_Value_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Binary_Value_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Binary_Value_Description(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_RELIABILITY Binary_Value_Reliability(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Value_Reliability_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_RELIABILITY value);
|
||||
|
||||
char *Binary_Value_Inactive_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Inactive_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
char *Binary_Value_Active_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Active_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
int Binary_Value_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
|
||||
bool Binary_Value_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
bool Binary_Value_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
bool Binary_Value_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Binary_Value_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
|
||||
BACNET_BINARY_PV Binary_Value_Present_Value(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Present_Value_Set(
|
||||
uint32_t instance,
|
||||
BACNET_BINARY_PV value);
|
||||
|
||||
bool Binary_Value_Out_Of_Service(
|
||||
uint32_t instance);
|
||||
void Binary_Value_Out_Of_Service_Set(
|
||||
uint32_t instance,
|
||||
bool value);
|
||||
|
||||
char *Binary_Value_Description(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Description_Set(
|
||||
uint32_t object_instance,
|
||||
char *text_string);
|
||||
|
||||
char *Binary_Value_Inactive_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Inactive_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
char *Binary_Value_Active_Text(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Active_Text_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
BACNET_POLARITY Binary_Value_Polarity(
|
||||
uint32_t instance);
|
||||
bool Binary_Value_Polarity_Set(
|
||||
uint32_t object_instance,
|
||||
BACNET_POLARITY polarity);
|
||||
bool Binary_Value_Create(
|
||||
uint32_t object_instance);
|
||||
bool Binary_Value_Delete(
|
||||
uint32_t object_instance);
|
||||
void Binary_Value_Cleanup(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testBinary_Value(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I.
|
||||
DEFINES = -DBIG_ENDIAN=0 -DTEST -DBACAPP_ALL -DTEST_BINARY_VALUE
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = bv.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = binary_value
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Steve Karg
|
||||
* @date 2013
|
||||
* @brief Channel objects, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Channel object is a command object without a priority array, and the
|
||||
* present-value property uses a priority array and a single precision floating point
|
||||
* data type.
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef CHANNEL_H
|
||||
#define CHANNEL_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/basic/object/lo.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/* BACNET_CHANNEL_VALUE decodes WriteProperty service requests
|
||||
Choose the datatypes that your application supports */
|
||||
#if !(defined(CHANNEL_NUMERIC) || \
|
||||
defined(CHANNEL_NULL) || \
|
||||
defined(CHANNEL_BOOLEAN) || \
|
||||
defined(CHANNEL_UNSIGNED) || \
|
||||
defined(CHANNEL_SIGNED) || \
|
||||
defined(CHANNEL_REAL) || \
|
||||
defined(CHANNEL_DOUBLE) || \
|
||||
defined(CHANNEL_OCTET_STRING) || \
|
||||
defined(CHANNEL_CHARACTER_STRING) || \
|
||||
defined(CHANNEL_BIT_STRING) || \
|
||||
defined(CHANNEL_ENUMERATED) || \
|
||||
defined(CHANNEL_DATE) || \
|
||||
defined(CHANNEL_TIME) || \
|
||||
defined(CHANNEL_OBJECT_ID) || \
|
||||
defined(CHANNEL_LIGHTING_COMMAND))
|
||||
#define CHANNEL_NUMERIC
|
||||
#endif
|
||||
|
||||
#if defined (CHANNEL_NUMERIC)
|
||||
#define CHANNEL_NULL
|
||||
#define CHANNEL_BOOLEAN
|
||||
#define CHANNEL_UNSIGNED
|
||||
#define CHANNEL_SIGNED
|
||||
#define CHANNEL_REAL
|
||||
#define CHANNEL_DOUBLE
|
||||
#define CHANNEL_ENUMERATED
|
||||
#define CHANNEL_LIGHTING_COMMAND
|
||||
#endif
|
||||
|
||||
typedef struct BACnet_Channel_Value_t {
|
||||
uint8_t tag;
|
||||
union {
|
||||
/* NULL - not needed as it is encoded in the tag alone */
|
||||
#if defined (CHANNEL_BOOLEAN)
|
||||
bool Boolean;
|
||||
#endif
|
||||
#if defined (CHANNEL_UNSIGNED)
|
||||
uint32_t Unsigned_Int;
|
||||
#endif
|
||||
#if defined (CHANNEL_SIGNED)
|
||||
int32_t Signed_Int;
|
||||
#endif
|
||||
#if defined (CHANNEL_REAL)
|
||||
float Real;
|
||||
#endif
|
||||
#if defined (CHANNEL_DOUBLE)
|
||||
double Double;
|
||||
#endif
|
||||
#if defined (CHANNEL_OCTET_STRING)
|
||||
BACNET_OCTET_STRING Octet_String;
|
||||
#endif
|
||||
#if defined (CHANNEL_CHARACTER_STRING)
|
||||
BACNET_CHARACTER_STRING Character_String;
|
||||
#endif
|
||||
#if defined (CHANNEL_BIT_STRING)
|
||||
BACNET_BIT_STRING Bit_String;
|
||||
#endif
|
||||
#if defined (CHANNEL_ENUMERATED)
|
||||
uint32_t Enumerated;
|
||||
#endif
|
||||
#if defined (CHANNEL_DATE)
|
||||
BACNET_DATE Date;
|
||||
#endif
|
||||
#if defined (CHANNEL_TIME)
|
||||
BACNET_TIME Time;
|
||||
#endif
|
||||
#if defined (CHANNEL_OBJECT_ID)
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
#endif
|
||||
#if defined (CHANNEL_LIGHTING_COMMAND)
|
||||
BACNET_LIGHTING_COMMAND Lighting_Command;
|
||||
#endif
|
||||
} type;
|
||||
/* simple linked list if needed */
|
||||
struct BACnet_Channel_Value_t *next;
|
||||
} BACNET_CHANNEL_VALUE;
|
||||
|
||||
void Channel_Property_Lists(const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
bool Channel_Valid_Instance(uint32_t object_instance);
|
||||
unsigned Channel_Count(void);
|
||||
uint32_t Channel_Index_To_Instance(unsigned index);
|
||||
unsigned Channel_Instance_To_Index(uint32_t instance);
|
||||
bool Channel_Object_Instance_Add(uint32_t instance);
|
||||
|
||||
bool Channel_Object_Name(uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Channel_Name_Set(uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
int Channel_Read_Property(BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Channel_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
BACNET_CHANNEL_VALUE * Channel_Present_Value(uint32_t object_instance);
|
||||
bool Channel_Present_Value_Set(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
bool Channel_Out_Of_Service(uint32_t object_instance);
|
||||
void Channel_Out_Of_Service_Set(uint32_t object_instance,
|
||||
bool oos_flag);
|
||||
|
||||
unsigned Channel_Last_Priority(uint32_t object_instance);
|
||||
BACNET_WRITE_STATUS Channel_Write_Status(uint32_t object_instance);
|
||||
uint16_t Channel_Number(uint32_t object_instance);
|
||||
bool Channel_Number_Set(uint32_t object_instance, uint16_t value);
|
||||
|
||||
unsigned Channel_Reference_List_Member_Count(uint32_t object_instance);
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *
|
||||
Channel_Reference_List_Member_Element(uint32_t object_instance,
|
||||
unsigned element);
|
||||
bool Channel_Reference_List_Member_Element_Set(uint32_t object_instance,
|
||||
unsigned array_index,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
|
||||
unsigned Channel_Reference_List_Member_Element_Add(uint32_t object_instance,
|
||||
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMemberSrc);
|
||||
unsigned Channel_Reference_List_Member_Local_Add(
|
||||
uint32_t object_instance,
|
||||
uint16_t type,
|
||||
uint32_t instance,
|
||||
BACNET_PROPERTY_ID propertyIdentifier,
|
||||
uint32_t arrayIndex);
|
||||
uint16_t Channel_Control_Groups_Element(
|
||||
uint32_t object_instance,
|
||||
int32_t array_index);
|
||||
bool Channel_Control_Groups_Element_Set(
|
||||
uint32_t object_instance,
|
||||
int32_t array_index,
|
||||
uint16_t value);
|
||||
bool Channel_Value_Copy(BACNET_CHANNEL_VALUE * cvalue,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
int Channel_Value_Encode(uint8_t *apdu, int apdu_max,
|
||||
BACNET_CHANNEL_VALUE * value);
|
||||
int Channel_Coerce_Data_Encode(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu,
|
||||
BACNET_APPLICATION_DATA_VALUE * value,
|
||||
BACNET_APPLICATION_TAG tag);
|
||||
bool Channel_Write_Member_Value(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data,
|
||||
BACNET_APPLICATION_DATA_VALUE * value);
|
||||
|
||||
void Channel_Init(void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testChannelObject(Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,918 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Nikola Jelic
|
||||
* @date 2014
|
||||
* @brief Command objects, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Command object type defines a standardized object whose
|
||||
* properties represent the externally visible characteristics of a
|
||||
* multi-action command procedure. A Command object is used to
|
||||
* write a set of values to a group of object properties, based on
|
||||
* the "action code" that is written to the Present_Value of the
|
||||
* Command object. Whenever the Present_Value property of the
|
||||
* Command object is written to, it triggers the Command object
|
||||
* to take a set of actions that change the values of a set of other
|
||||
* objects' properties.
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/config.h" /* the custom stuff */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/proplist.h"
|
||||
#include "bacnet/timestamp.h"
|
||||
#include "bacnet/basic/object/command.h"
|
||||
|
||||
/*BACnetActionCommand ::= SEQUENCE {
|
||||
deviceIdentifier [0] BACnetObjectIdentifier OPTIONAL,
|
||||
objectIdentifier [1] BACnetObjectIdentifier,
|
||||
propertyIdentifier [2] BACnetPropertyIdentifier,
|
||||
propertyArrayIndex [3] Unsigned OPTIONAL, --used only with array datatype
|
||||
propertyValue [4] ABSTRACT-SYNTAX.&Type,
|
||||
priority [5] Unsigned (1..16) OPTIONAL, --used only when property is commandable
|
||||
postDelay [6] Unsigned OPTIONAL,
|
||||
quitOnFailure [7] BOOLEAN,
|
||||
writeSuccessful [8] BOOLEAN
|
||||
}*/
|
||||
|
||||
int cl_encode_apdu(uint8_t *apdu, BACNET_ACTION_LIST *bcl)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
|
||||
if (bcl->Device_Id.instance >= 0 &&
|
||||
bcl->Device_Id.instance <= BACNET_MAX_INSTANCE) {
|
||||
len = encode_context_object_id(
|
||||
&apdu[apdu_len], 0, bcl->Device_Id.type, bcl->Device_Id.instance);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
/* TODO: Check for object type and instance limits */
|
||||
len = encode_context_object_id(
|
||||
&apdu[apdu_len], 1, bcl->Object_Id.type, bcl->Object_Id.instance);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
len =
|
||||
encode_context_enumerated(&apdu[apdu_len], 2, bcl->Property_Identifier);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (bcl->Property_Array_Index != BACNET_ARRAY_ALL) {
|
||||
len = encode_context_unsigned(
|
||||
&apdu[apdu_len], 3, bcl->Property_Array_Index);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
/* BACnet Testing Observed Incident oi00108
|
||||
Command Action not correctly formatted
|
||||
Revealed by BACnet Test Client v1.8.16 (
|
||||
www.bac-test.com/bacnet-test-client-download ) BITS: BIT00031 BC
|
||||
135.1: 9.20.1.7 BC 135.1: 9.20.1.9 Any discussions can be directed to
|
||||
edward@bac-test.com Please feel free to remove this comment when my
|
||||
changes have been reviewed by all interested parties. Say 6 months ->
|
||||
September 2016 */
|
||||
|
||||
len = encode_opening_tag(&apdu[apdu_len], 4);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
len = bacapp_encode_application_data(&apdu[apdu_len], &bcl->Value);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
len = encode_closing_tag(&apdu[apdu_len], 4);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
if (bcl->Priority != BACNET_NO_PRIORITY) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 5, bcl->Priority);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
if (bcl->Post_Delay != 0xFFFFFFFFU) {
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 6, bcl->Post_Delay);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
len = encode_context_boolean(&apdu[apdu_len], 7, bcl->Quit_On_Failure);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
len = encode_context_boolean(&apdu[apdu_len], 8, bcl->Write_Successful);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
int cl_decode_apdu(uint8_t *apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_APPLICATION_TAG tag,
|
||||
BACNET_ACTION_LIST *bcl)
|
||||
{
|
||||
int len = 0;
|
||||
int dec_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value_type = 0;
|
||||
|
||||
if (decode_is_context_tag(&apdu[dec_len], 0)) {
|
||||
/* Tag 0: Device ID */
|
||||
dec_len++;
|
||||
len = decode_object_id(
|
||||
&apdu[dec_len], &bcl->Device_Id.type, &bcl->Device_Id.instance);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
}
|
||||
if (!decode_is_context_tag(&apdu[dec_len++], 1)) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = decode_object_id(
|
||||
&apdu[dec_len], &bcl->Object_Id.type, &bcl->Object_Id.instance);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
len = decode_tag_number_and_value(
|
||||
&apdu[dec_len], &tag_number, &len_value_type);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
if (tag_number != 2) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = decode_enumerated(
|
||||
&apdu[dec_len], len_value_type, &bcl->Property_Identifier);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
if (decode_is_context_tag(&apdu[dec_len], 3)) {
|
||||
len = decode_tag_number_and_value(
|
||||
&apdu[dec_len], &tag_number, &len_value_type);
|
||||
dec_len += len;
|
||||
len = decode_unsigned(
|
||||
&apdu[dec_len], len_value_type, &bcl->Property_Array_Index);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
} else {
|
||||
bcl->Property_Array_Index = BACNET_ARRAY_ALL;
|
||||
}
|
||||
if (!decode_is_context_tag(&apdu[dec_len], 4)) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
bcl->Value.context_specific = true;
|
||||
bcl->Value.context_tag = 4;
|
||||
bcl->Value.tag = tag;
|
||||
switch (tag) {
|
||||
case BACNET_APPLICATION_TAG_NULL:
|
||||
len = 1;
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_BOOLEAN:
|
||||
len = decode_context_boolean2(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Boolean);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_UNSIGNED_INT:
|
||||
len = decode_context_unsigned(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Unsigned_Int);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_SIGNED_INT:
|
||||
len = decode_context_signed(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Signed_Int);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_REAL:
|
||||
len = decode_context_real(&apdu[dec_len], 4, &bcl->Value.type.Real);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DOUBLE:
|
||||
len = decode_context_double(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Double);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_OCTET_STRING:
|
||||
len = decode_context_octet_string(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Octet_String);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_CHARACTER_STRING:
|
||||
len = decode_context_character_string(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Character_String);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_BIT_STRING:
|
||||
len = decode_context_bitstring(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Bit_String);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_ENUMERATED:
|
||||
len = decode_context_enumerated(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Enumerated);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DATE:
|
||||
len = decode_context_date(&apdu[dec_len], 4, &bcl->Value.type.Date);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_TIME:
|
||||
len = decode_context_bacnet_time(
|
||||
&apdu[dec_len], 4, &bcl->Value.type.Time);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_OBJECT_ID:
|
||||
len = decode_context_object_id(&apdu[dec_len], 4,
|
||||
&bcl->Value.type.Object_Id.type,
|
||||
&bcl->Value.type.Object_Id.instance);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
|
||||
len = lighting_command_decode(&apdu[dec_len], apdu_len - dec_len,
|
||||
&bcl->Value.type.Lighting_Command);
|
||||
break;
|
||||
default:
|
||||
return BACNET_STATUS_REJECT;
|
||||
break;
|
||||
}
|
||||
if (len > 0) {
|
||||
dec_len += len;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[dec_len], 5)) {
|
||||
uint32_t priority_dec;
|
||||
len = decode_tag_number_and_value(
|
||||
&apdu[dec_len], &tag_number, &len_value_type);
|
||||
dec_len += len;
|
||||
len = decode_unsigned(&apdu[dec_len], len_value_type, &priority_dec);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
bcl->Priority = (uint8_t)priority_dec;
|
||||
dec_len += len;
|
||||
} else {
|
||||
bcl->Priority = BACNET_NO_PRIORITY;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[dec_len], 6)) {
|
||||
len = decode_tag_number_and_value(
|
||||
&apdu[dec_len], &tag_number, &len_value_type);
|
||||
dec_len += len;
|
||||
len = decode_unsigned(&apdu[dec_len], len_value_type, &bcl->Post_Delay);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
} else {
|
||||
bcl->Post_Delay = 0xFFFFFFFFU;
|
||||
}
|
||||
if (!decode_is_context_tag(&apdu[dec_len], 7)) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = decode_context_boolean2(&apdu[dec_len], 7, &bcl->Quit_On_Failure);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
if (!decode_is_context_tag(&apdu[dec_len], 8)) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = decode_context_boolean2(&apdu[dec_len], 8, &bcl->Write_Successful);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
dec_len += len;
|
||||
if (dec_len < apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
|
||||
return dec_len;
|
||||
}
|
||||
|
||||
COMMAND_DESCR Command_Descr[MAX_COMMANDS];
|
||||
|
||||
/* These arrays are used by the ReadPropertyMultiple handler */
|
||||
static const int Command_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_IN_PROCESS,
|
||||
PROP_ALL_WRITES_SUCCESSFUL, PROP_ACTION, -1 };
|
||||
|
||||
static const int Command_Properties_Optional[] = { PROP_DESCRIPTION, -1 };
|
||||
|
||||
static const int Command_Properties_Proprietary[] = { -1 };
|
||||
|
||||
/**
|
||||
* Returns the list of required, optional, and proprietary properties.
|
||||
* Used by ReadPropertyMultiple service.
|
||||
*
|
||||
* @param pRequired - pointer to list of int terminated by -1, of
|
||||
* BACnet required properties for this object.
|
||||
* @param pOptional - pointer to list of int terminated by -1, of
|
||||
* BACnet optkional properties for this object.
|
||||
* @param pProprietary - pointer to list of int terminated by -1, of
|
||||
* BACnet proprietary properties for this object.
|
||||
*/
|
||||
void Command_Property_Lists(
|
||||
const int **pRequired, const int **pOptional, const int **pProprietary)
|
||||
{
|
||||
if (pRequired) {
|
||||
*pRequired = Command_Properties_Required;
|
||||
}
|
||||
if (pOptional) {
|
||||
*pOptional = Command_Properties_Optional;
|
||||
}
|
||||
if (pProprietary) {
|
||||
*pProprietary = Command_Properties_Proprietary;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Command object data
|
||||
*/
|
||||
void Command_Init(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < MAX_COMMANDS; i++) {
|
||||
Command_Descr[i].Present_Value = 0;
|
||||
Command_Descr[i].In_Process = false;
|
||||
Command_Descr[i].All_Writes_Successful = true; /* Optimistic default */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given object instance is valid
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return true if the instance is valid, and false if not
|
||||
*/
|
||||
bool Command_Valid_Instance(uint32_t object_instance)
|
||||
{
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the number of this object instances
|
||||
*
|
||||
* @return Number of objects
|
||||
*/
|
||||
unsigned Command_Count(void)
|
||||
{
|
||||
return MAX_COMMANDS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the object instance-number for a given 0..N index
|
||||
* of objects where N is the total number of this object instances.
|
||||
*
|
||||
* @param index - 0..total number of this object instances
|
||||
*
|
||||
* @return object instance-number for the given index
|
||||
*/
|
||||
uint32_t Command_Index_To_Instance(unsigned index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines a 0..N index
|
||||
* of this object where N is the total number of this object instances
|
||||
*
|
||||
* @param object_instance - object-instance number of the object.
|
||||
*
|
||||
* @return index for the given instance-number, or
|
||||
* the total number of this object instances if not valid.
|
||||
*/
|
||||
unsigned Command_Instance_To_Index(uint32_t object_instance)
|
||||
{
|
||||
unsigned index = MAX_COMMANDS;
|
||||
|
||||
if (object_instance < MAX_COMMANDS) {
|
||||
index = object_instance;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return present-value of the object
|
||||
*/
|
||||
uint32_t Command_Present_Value(uint32_t object_instance)
|
||||
{
|
||||
uint32_t value = 0;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
value = Command_Descr[index].Present_Value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the present-value
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - present-value to set
|
||||
*
|
||||
* @return true if values are within range and present-value is set.
|
||||
*/
|
||||
bool Command_Present_Value_Set(uint32_t object_instance, uint32_t value)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
Command_Descr[index].Present_Value = value;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, determines if the command
|
||||
* is in-process. This true value indicates that the Command object has
|
||||
* begun processing one of a set of action sequences. Once all of the
|
||||
* writes have been attempted by the Command object, the In_Process
|
||||
* property shall be set back to false.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return true if this object-instance is in-process.
|
||||
*/
|
||||
bool Command_In_Process(uint32_t object_instance)
|
||||
{
|
||||
bool value = false;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
value = Command_Descr[index].In_Process;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the command in-process value.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - true or false value to set
|
||||
*
|
||||
* @return true if values are within range and in-process flag is set.
|
||||
*/
|
||||
bool Command_In_Process_Set(uint32_t object_instance, bool value)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
Command_Descr[index].In_Process = value;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, indicates the success or failure
|
||||
* of the sequence of actions that are triggered when the Present_Value
|
||||
* property is written to.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
*
|
||||
* @return true if all writes were successful for this object-instance
|
||||
*/
|
||||
bool Command_All_Writes_Successful(uint32_t object_instance)
|
||||
{
|
||||
bool value = false;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
value = Command_Descr[index].All_Writes_Successful;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, sets the all-writes-successful value.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param value - true or false value to set
|
||||
*
|
||||
* @return true if values are within range and all-writes-succcessful is set.
|
||||
*/
|
||||
bool Command_All_Writes_Successful_Set(uint32_t object_instance, bool value)
|
||||
{
|
||||
bool status = false;
|
||||
unsigned int index;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
Command_Descr[index].All_Writes_Successful = value;
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given object instance-number, loads the object-name into
|
||||
* a characterstring.
|
||||
*
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @param object_name - holds the object-name retrieved
|
||||
*
|
||||
* @return true if object-name was retrieved
|
||||
*/
|
||||
bool Command_Object_Name(
|
||||
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
|
||||
{
|
||||
static char text_string[32] = ""; /* okay for single thread */
|
||||
unsigned int index;
|
||||
bool status = false;
|
||||
|
||||
index = Command_Instance_To_Index(object_instance);
|
||||
if (index < MAX_COMMANDS) {
|
||||
sprintf(text_string, "COMMAND %lu", (unsigned long)index);
|
||||
status = characterstring_init_ansi(object_name, text_string);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* ReadProperty handler for this object. For the given ReadProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
*
|
||||
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
*
|
||||
* @return number of APDU bytes in the response, or
|
||||
* BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int Command_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
|
||||
{
|
||||
int apdu_len = 0; /* return value */
|
||||
int len = 0;
|
||||
BACNET_CHARACTER_STRING char_string;
|
||||
unsigned object_index = 0;
|
||||
uint8_t *apdu = NULL;
|
||||
uint16_t apdu_max = 0;
|
||||
COMMAND_DESCR *CurrentCommand;
|
||||
|
||||
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
|
||||
(rpdata->application_data_len == 0)) {
|
||||
return 0;
|
||||
}
|
||||
apdu_max = rpdata->application_data_len;
|
||||
object_index = Command_Instance_To_Index(rpdata->object_instance);
|
||||
if (object_index < MAX_COMMANDS) {
|
||||
CurrentCommand = &Command_Descr[object_index];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
apdu = rpdata->application_data;
|
||||
switch ((int)rpdata->object_property) {
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
apdu_len = encode_application_object_id(
|
||||
&apdu[0], OBJECT_COMMAND, rpdata->object_instance);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_DESCRIPTION:
|
||||
Command_Object_Name(rpdata->object_instance, &char_string);
|
||||
apdu_len =
|
||||
encode_application_character_string(&apdu[0], &char_string);
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_TYPE:
|
||||
apdu_len = encode_application_enumerated(&apdu[0], OBJECT_COMMAND);
|
||||
break;
|
||||
|
||||
case PROP_PRESENT_VALUE:
|
||||
apdu_len = encode_application_unsigned(
|
||||
&apdu[0], Command_Present_Value(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_IN_PROCESS:
|
||||
apdu_len = encode_application_boolean(
|
||||
&apdu[0], Command_In_Process(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_ALL_WRITES_SUCCESSFUL:
|
||||
apdu_len = encode_application_boolean(&apdu[0],
|
||||
Command_All_Writes_Successful(rpdata->object_instance));
|
||||
break;
|
||||
case PROP_ACTION:
|
||||
/* TODO */
|
||||
if (rpdata->array_index == 0) {
|
||||
apdu_len =
|
||||
encode_application_unsigned(&apdu[0], MAX_COMMAND_ACTIONS);
|
||||
} else if (rpdata->array_index == BACNET_ARRAY_ALL) {
|
||||
int i;
|
||||
for (i = 0; i < MAX_COMMAND_ACTIONS; i++) {
|
||||
BACNET_ACTION_LIST *Curr_CL_Member =
|
||||
&CurrentCommand->Action[0];
|
||||
/* another loop, for aditional actions in the list */
|
||||
for (; Curr_CL_Member != NULL;
|
||||
Curr_CL_Member = Curr_CL_Member->next) {
|
||||
len = cl_encode_apdu(
|
||||
&apdu[apdu_len], &CurrentCommand->Action[0]);
|
||||
apdu_len += len;
|
||||
/* assume the next one is of the same length, which need
|
||||
* not be the case */
|
||||
if ((i != MAX_COMMAND_ACTIONS - 1) &&
|
||||
(apdu_len + len) >= apdu_max) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (rpdata->array_index < MAX_COMMAND_ACTIONS) {
|
||||
BACNET_ACTION_LIST *Curr_CL_Member =
|
||||
&CurrentCommand->Action[rpdata->array_index];
|
||||
/* another loop, for aditional actions in the list */
|
||||
for (; Curr_CL_Member != NULL;
|
||||
Curr_CL_Member = Curr_CL_Member->next) {
|
||||
len = cl_encode_apdu(
|
||||
&apdu[apdu_len], &CurrentCommand->Action[0]);
|
||||
apdu_len += len;
|
||||
/* assume the next one is of the same length, which need
|
||||
* not be the case */
|
||||
if ((apdu_len + len) >= apdu_max) {
|
||||
rpdata->error_code =
|
||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||
apdu_len = BACNET_STATUS_ABORT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((apdu_len >= 0) && (rpdata->object_property != PROP_ACTION) &&
|
||||
(rpdata->array_index != BACNET_ARRAY_ALL)) {
|
||||
rpdata->error_class = ERROR_CLASS_PROPERTY;
|
||||
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
apdu_len = BACNET_STATUS_ERROR;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* WriteProperty handler for this object. For the given WriteProperty
|
||||
* data, the application_data is loaded or the error flags are set.
|
||||
*
|
||||
* @param wp_data - BACNET_WRITE_PROPERTY_DATA data, including
|
||||
* requested data and space for the reply, or error response.
|
||||
*
|
||||
* @return false if an error is loaded, true if no errors
|
||||
*/
|
||||
bool Command_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
|
||||
{
|
||||
bool status = false; /* return value */
|
||||
unsigned int object_index = 0;
|
||||
int len = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE value;
|
||||
/* decode the some of the request */
|
||||
len = bacapp_decode_application_data(
|
||||
wp_data->application_data, wp_data->application_data_len, &value);
|
||||
/* FIXME: len < application_data_len: more data? */
|
||||
if (len < 0) {
|
||||
/* error while decoding - a value larger than we can handle */
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
/* only array properties can have array options */
|
||||
if ((wp_data->object_property != PROP_ACTION) &&
|
||||
(wp_data->array_index != BACNET_ARRAY_ALL)) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
|
||||
return false;
|
||||
}
|
||||
object_index = Command_Instance_To_Index(wp_data->object_instance);
|
||||
if (object_index >= MAX_COMMANDS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ((int)wp_data->object_property) {
|
||||
case PROP_PRESENT_VALUE:
|
||||
status =
|
||||
WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT,
|
||||
&wp_data->error_class, &wp_data->error_code);
|
||||
if (status) {
|
||||
if (value.type.Unsigned_Int >= MAX_COMMAND_ACTIONS) {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
|
||||
return false;
|
||||
}
|
||||
Command_Present_Value_Set(
|
||||
wp_data->object_instance, value.type.Unsigned_Int);
|
||||
} else {
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
status = false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case PROP_OBJECT_IDENTIFIER:
|
||||
case PROP_OBJECT_NAME:
|
||||
case PROP_OBJECT_TYPE:
|
||||
case PROP_IN_PROCESS:
|
||||
case PROP_ALL_WRITES_SUCCESSFUL:
|
||||
case PROP_ACTION:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
break;
|
||||
default:
|
||||
wp_data->error_class = ERROR_CLASS_PROPERTY;
|
||||
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void Command_Intrinsic_Reporting(uint32_t object_instance)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "ctest.h"
|
||||
|
||||
bool WPValidateArgType(BACNET_APPLICATION_DATA_VALUE *pValue,
|
||||
uint8_t ucExpectedTag,
|
||||
BACNET_ERROR_CLASS *pErrorClass,
|
||||
BACNET_ERROR_CODE *pErrorCode)
|
||||
{
|
||||
bool bResult;
|
||||
|
||||
/*
|
||||
* start out assuming success and only set up error
|
||||
* response if validation fails.
|
||||
*/
|
||||
bResult = true;
|
||||
if (pValue->tag != ucExpectedTag) {
|
||||
bResult = false;
|
||||
*pErrorClass = ERROR_CLASS_PROPERTY;
|
||||
*pErrorCode = ERROR_CODE_INVALID_DATA_TYPE;
|
||||
}
|
||||
|
||||
return (bResult);
|
||||
}
|
||||
|
||||
void testCommand(Test *pTest)
|
||||
{
|
||||
uint8_t apdu[MAX_APDU] = { 0 };
|
||||
int len = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t decoded_instance = 0;
|
||||
uint16_t decoded_type = 0;
|
||||
BACNET_READ_PROPERTY_DATA rpdata;
|
||||
BACNET_ACTION_LIST clist, clist_test;
|
||||
Command_Init();
|
||||
rpdata.application_data = &apdu[0];
|
||||
rpdata.application_data_len = sizeof(apdu);
|
||||
rpdata.object_type = OBJECT_COMMAND;
|
||||
rpdata.object_instance = 1;
|
||||
rpdata.object_property = PROP_OBJECT_IDENTIFIER;
|
||||
rpdata.array_index = BACNET_ARRAY_ALL;
|
||||
len = Command_Read_Property(&rpdata);
|
||||
ct_test(pTest, len != 0);
|
||||
len = decode_tag_number_and_value(&apdu[0], &tag_number, &len_value);
|
||||
ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OBJECT_ID);
|
||||
len = decode_object_id(&apdu[len], &decoded_type, &decoded_instance);
|
||||
ct_test(pTest, decoded_type == rpdata.object_type);
|
||||
ct_test(pTest, decoded_instance == rpdata.object_instance);
|
||||
memset(&clist, 0, sizeof(BACNET_ACTION_LIST));
|
||||
memset(&clist_test, 0, sizeof(BACNET_ACTION_LIST));
|
||||
clist.Device_Id.type = OBJECT_DEVICE;
|
||||
clist.Device_Id.instance = 3389;
|
||||
clist.Object_Id.type = OBJECT_ANALOG_VALUE;
|
||||
clist.Object_Id.instance = 42;
|
||||
clist.Property_Identifier = PROP_PRESENT_VALUE;
|
||||
clist.Property_Array_Index = BACNET_ARRAY_ALL;
|
||||
clist.Value.tag = BACNET_APPLICATION_TAG_REAL;
|
||||
clist.Value.type.Real = 39.0f;
|
||||
clist.Priority = 4;
|
||||
clist.Post_Delay = 0xFFFFFFFFU;
|
||||
clist.Quit_On_Failure = true;
|
||||
clist.Write_Successful = false;
|
||||
clist.next = NULL;
|
||||
len = cl_encode_apdu(apdu, &clist);
|
||||
ct_test(pTest, len > 0);
|
||||
len = cl_decode_apdu(apdu, len, BACNET_APPLICATION_TAG_REAL, &clist_test);
|
||||
ct_test(pTest, len > 0);
|
||||
ct_test(pTest, clist.Device_Id.type == clist_test.Device_Id.type);
|
||||
ct_test(pTest, clist.Device_Id.instance == clist_test.Device_Id.instance);
|
||||
ct_test(pTest, clist.Object_Id.type == clist_test.Object_Id.type);
|
||||
ct_test(pTest, clist.Object_Id.instance == clist_test.Object_Id.instance);
|
||||
ct_test(pTest, clist.Property_Identifier == clist_test.Property_Identifier);
|
||||
ct_test(
|
||||
pTest, clist.Property_Array_Index == clist_test.Property_Array_Index);
|
||||
ct_test(pTest, clist.Value.tag == clist_test.Value.tag);
|
||||
ct_test(pTest, clist.Value.type.Real == clist_test.Value.type.Real);
|
||||
ct_test(pTest, clist.Priority == clist_test.Priority);
|
||||
ct_test(pTest, clist.Post_Delay == clist_test.Post_Delay);
|
||||
ct_test(pTest, clist.Quit_On_Failure == clist_test.Quit_On_Failure);
|
||||
ct_test(pTest, clist.Write_Successful == clist_test.Write_Successful);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TEST_COMMAND
|
||||
int main(void)
|
||||
{
|
||||
Test *pTest;
|
||||
bool rc;
|
||||
|
||||
pTest = ct_create("BACnet Command", NULL);
|
||||
/* individual tests */
|
||||
rc = ct_addTestFunction(pTest, testCommand);
|
||||
assert(rc);
|
||||
|
||||
ct_setStream(pTest, stdout);
|
||||
ct_run(pTest);
|
||||
(void)ct_report(pTest);
|
||||
ct_destroy(pTest);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* TEST_COMMAND */
|
||||
#endif /* TEST */
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* @file
|
||||
* @author Nikola Jelic
|
||||
* @date 2014
|
||||
* @brief Command objects, customize for your use
|
||||
*
|
||||
* @section DESCRIPTION
|
||||
*
|
||||
* The Command object type defines a standardized object whose
|
||||
* properties represent the externally visible characteristics of a
|
||||
* multi-action command procedure. A Command object is used to
|
||||
* write a set of values to a group of object properties, based on
|
||||
* the "action code" that is written to the Present_Value of the
|
||||
* Command object. Whenever the Present_Value property of the
|
||||
* Command object is written to, it triggers the Command object
|
||||
* to take a set of actions that change the values of a set of other
|
||||
* objects' properties.
|
||||
*
|
||||
* @section LICENSE
|
||||
*
|
||||
* Copyright (C) 2014 Nikola Jelic <nikola.jelic@euroicc.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/rp.h"
|
||||
#include "bacnet/wp.h"
|
||||
|
||||
#ifndef MAX_COMMANDS
|
||||
#define MAX_COMMANDS 4
|
||||
#endif
|
||||
|
||||
#ifndef MAX_COMMAND_ACTIONS
|
||||
#define MAX_COMMAND_ACTIONS 8
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
typedef struct bacnet_action_list {
|
||||
BACNET_OBJECT_ID Device_Id; /* Optional */
|
||||
BACNET_OBJECT_ID Object_Id;
|
||||
BACNET_PROPERTY_ID Property_Identifier;
|
||||
uint32_t Property_Array_Index; /* Conditional */
|
||||
BACNET_APPLICATION_DATA_VALUE Value;
|
||||
uint8_t Priority; /* Conditional */
|
||||
uint32_t Post_Delay; /* Optional */
|
||||
bool Quit_On_Failure;
|
||||
bool Write_Successful;
|
||||
struct bacnet_action_list *next;
|
||||
} BACNET_ACTION_LIST;
|
||||
|
||||
int cl_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
BACNET_ACTION_LIST * bcl);
|
||||
|
||||
int cl_decode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned apdu_len,
|
||||
BACNET_APPLICATION_TAG tag,
|
||||
BACNET_ACTION_LIST * bcl);
|
||||
|
||||
typedef struct command_descr {
|
||||
uint32_t Present_Value;
|
||||
bool In_Process;
|
||||
bool All_Writes_Successful;
|
||||
BACNET_ACTION_LIST Action[MAX_COMMAND_ACTIONS];
|
||||
} COMMAND_DESCR;
|
||||
|
||||
void Command_Property_Lists(
|
||||
const int **pRequired,
|
||||
const int **pOptional,
|
||||
const int **pProprietary);
|
||||
|
||||
bool Command_Valid_Instance(
|
||||
uint32_t object_instance);
|
||||
unsigned Command_Count(
|
||||
void);
|
||||
uint32_t Command_Index_To_Instance(
|
||||
unsigned index);
|
||||
unsigned Command_Instance_To_Index(
|
||||
uint32_t instance);
|
||||
bool Command_Object_Instance_Add(
|
||||
uint32_t instance);
|
||||
|
||||
bool Command_Object_Name(
|
||||
uint32_t object_instance,
|
||||
BACNET_CHARACTER_STRING * object_name);
|
||||
bool Command_Name_Set(
|
||||
uint32_t object_instance,
|
||||
char *new_name);
|
||||
|
||||
char *Command_Description(
|
||||
uint32_t instance);
|
||||
bool Command_Description_Set(
|
||||
uint32_t instance,
|
||||
char *new_name);
|
||||
|
||||
int Command_Read_Property(
|
||||
BACNET_READ_PROPERTY_DATA * rpdata);
|
||||
bool Command_Write_Property(
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
uint32_t Command_Present_Value(
|
||||
uint32_t object_instance);
|
||||
bool Command_Present_Value_Set(
|
||||
uint32_t object_instance,
|
||||
uint32_t value);
|
||||
|
||||
bool Command_In_Process(
|
||||
uint32_t object_instance);
|
||||
bool Command_In_Process_Set(
|
||||
uint32_t object_instance,
|
||||
bool value);
|
||||
|
||||
bool Command_All_Writes_Successful(
|
||||
uint32_t object_instance);
|
||||
bool Command_All_Writes_Successful_Set(
|
||||
uint32_t object_instance,
|
||||
bool value);
|
||||
|
||||
bool Command_Change_Of_Value(
|
||||
uint32_t instance);
|
||||
void Command_Change_Of_Value_Clear(
|
||||
uint32_t instance);
|
||||
bool Command_Encode_Value_List(
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE * value_list);
|
||||
float Command_COV_Increment(
|
||||
uint32_t instance);
|
||||
void Command_COV_Increment_Set(
|
||||
uint32_t instance,
|
||||
float value);
|
||||
|
||||
/* note: header of Intrinsic_Reporting function is required
|
||||
even when INTRINSIC_REPORTING is not defined */
|
||||
void Command_Intrinsic_Reporting(
|
||||
uint32_t object_instance);
|
||||
|
||||
void Command_Init(
|
||||
void);
|
||||
|
||||
#ifdef TEST
|
||||
#include "ctest.h"
|
||||
void testCommand(
|
||||
Test * pTest);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
#Makefile to build test case
|
||||
CC = gcc
|
||||
SRC_DIR = ../../src
|
||||
TEST_DIR = ../../test
|
||||
HANDLER_DIR = ../handler
|
||||
INCLUDES = -I../../include -I$(TEST_DIR) -I. -I$(HANDLER_DIR)
|
||||
DEFINES = -DBIG_ENDIAN=0 -DBACDL_ALL -DTEST -DTEST_COMMAND
|
||||
|
||||
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
|
||||
|
||||
SRCS = command.c \
|
||||
$(SRC_DIR)/bacnet/bacdcode.c \
|
||||
$(SRC_DIR)/bacnet/bacint.c \
|
||||
$(SRC_DIR)/bacnet/bacstr.c \
|
||||
$(SRC_DIR)/bacnet/bacreal.c \
|
||||
$(SRC_DIR)/bacnet/bacapp.c \
|
||||
$(SRC_DIR)/bacnet/bacdevobjpropref.c \
|
||||
$(SRC_DIR)/bacnet/bactext.c \
|
||||
$(SRC_DIR)/bacnet/indtext.c \
|
||||
$(SRC_DIR)/bacnet/datetime.c \
|
||||
$(SRC_DIR)/bacnet/lighting.c \
|
||||
$(TEST_DIR)/ctest.c
|
||||
|
||||
TARGET = command
|
||||
|
||||
all: ${TARGET}
|
||||
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
${TARGET}: ${OBJS}
|
||||
${CC} -o $@ ${OBJS}
|
||||
|
||||
.c.o:
|
||||
${CC} -c ${CFLAGS} $*.c -o $@
|
||||
|
||||
depend:
|
||||
rm -f .depend
|
||||
${CC} -MM ${CFLAGS} *.c >> .depend
|
||||
|
||||
clean:
|
||||
rm -rf core ${TARGET} $(OBJS)
|
||||
|
||||
include: .depend
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user