Release BACnet Protocol Stack 0.3.0

This commit is contained in:
skarg
2007-02-23 13:19:57 +00:00
parent 6c5b409dcf
commit 63e18a0f1f
315 changed files with 57198 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
set print pretty on
set print union on
set print address on
list
+1
View File
@@ -0,0 +1 @@
-kr -nut -nlp
+56
View File
@@ -0,0 +1,56 @@
all: readprop writeprop readfile writefile reinit server dcc whohas whois ucov timesync
@echo "utilities are in the utils directory"
clean: \
demo/readprop/Makefile \
demo/writeprop/Makefile \
demo/readfile/Makefile \
demo/writefile/Makefile \
demo/server/Makefile \
demo/dcc/Makefile \
demo/whohas/Makefile \
demo/timesync/Makefile \
demo/whois/Makefile
( cd demo/readprop ; make clean )
( cd demo/writeprop ; make clean )
( cd demo/readfile ; make clean )
( cd demo/writefile ; make clean )
( cd demo/reinit ; make clean )
( cd demo/server ; make clean )
( cd demo/dcc ; make clean )
( cd demo/whohas ; make clean )
( cd demo/timesync ; make clean )
( cd demo/whois ; make clean )
readprop: demo/readprop/Makefile
( cd demo/readprop ; make clean ; make ; cp bacrp ../../utils )
writeprop: demo/writeprop/Makefile
( cd demo/writeprop ; make clean ; make ; cp bacwp ../../utils )
readfile: demo/readfile/Makefile
( cd demo/readfile ; make clean ; make ; cp bacarf ../../utils )
writefile: demo/writefile/Makefile
( cd demo/writefile ; make clean ; make ; cp bacawf ../../utils )
reinit: demo/reinit/Makefile
( cd demo/reinit ; make clean ; make ; cp bacrd ../../utils )
server: demo/server/Makefile
( cd demo/server ; make clean ; make ; cp bacserv ../../utils )
dcc: demo/dcc/Makefile
( cd demo/dcc ; make clean ; make ; cp bacdcc ../../utils )
whohas: demo/whohas/Makefile
( cd demo/whohas ; make clean ; make ; cp bacwh ../../utils )
timesync: demo/timesync/Makefile
( cd demo/timesync ; make clean ; make ; cp bacts ../../utils )
ucov: demo/ucov/Makefile
( cd demo/ucov ; make clean ; make ; cp bacucov ../../utils )
whois: demo/whois/Makefile
( cd demo/whois ; make clean ; make ; cp bacwi ../../utils )
+195
View File
@@ -0,0 +1,195 @@
/*####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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
/* 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;
}
/* 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) {
if (invoke_id)
*invoke_id = apdu[0];
if (abort_reason)
*abort_reason = apdu[1];
}
return len;
}
#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) {
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 */
+62
View File
@@ -0,0 +1,62 @@
/*####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 ABORT_H
#define ABORT_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
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
+34
View File
@@ -0,0 +1,34 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_ABORT -g
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
abort.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = abort
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+366
View File
@@ -0,0 +1,366 @@
/*####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####*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "config.h"
#include "address.h"
#include "bacdef.h"
#include "bacdcode.h"
/* This module is used to handle the address binding that */
/* occurs in BACnet. A device id is bound to a MAC address. */
/* The normal method is using Who-Is, and using the data from I-Am */
static struct Address_Cache_Entry {
bool valid;
bool bind_request;
uint32_t device_id;
unsigned max_apdu;
BACNET_ADDRESS address;
} Address_Cache[MAX_ADDRESS_CACHE];
void address_copy(BACNET_ADDRESS * dest, BACNET_ADDRESS * src)
{
unsigned i = 0; /* counter */
if (dest && src) {
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = src->mac[i];
}
dest->mac_len = src->mac_len;
dest->net = src->net;
dest->len = src->len;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = src->adr[i];
}
}
return;
}
void address_remove_device(uint32_t device_id)
{
unsigned i;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if ((Address_Cache[i].valid ||
Address_Cache[i].bind_request) &&
(Address_Cache[i].device_id == device_id)) {
Address_Cache[i].valid = false;
break;
}
}
return;
}
void address_init(void)
{
unsigned i;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
Address_Cache[i].valid = false;
Address_Cache[i].bind_request = false;
}
return;
}
bool address_get_by_device(uint32_t device_id,
unsigned *max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
address_copy(src, &Address_Cache[i].address);
*max_apdu = Address_Cache[i].max_apdu;
found = true;
break;
}
}
return found;
}
void address_add(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
address_copy(&Address_Cache[i].address, src);
Address_Cache[i].max_apdu = max_apdu;
found = true;
break;
}
}
/* new device */
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!Address_Cache[i].valid) {
Address_Cache[i].valid = true;
Address_Cache[i].device_id = device_id;
Address_Cache[i].max_apdu = max_apdu;
address_copy(&Address_Cache[i].address, src);
break;
}
}
}
return;
}
/* returns true if device is already bound */
/* also returns the address and max apdu if already bound */
bool address_bind_request(uint32_t device_id,
unsigned *max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
found = true;
address_copy(src, &Address_Cache[i].address);
*max_apdu = Address_Cache[i].max_apdu;
break;
}
/* already have a bind request active for this puppy */
else if (Address_Cache[i].bind_request &&
(Address_Cache[i].device_id == device_id)) {
return found;
}
}
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!(Address_Cache[i].bind_request || Address_Cache[i].valid)) {
Address_Cache[i].bind_request = true;
Address_Cache[i].device_id = device_id;
/* now would be a good time to do a Who-Is request */
break;
}
}
}
return found;
}
void address_add_binding(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src)
{
unsigned i;
bool found = false; /* return value */
/* existing device - update address */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid &&
(Address_Cache[i].device_id == device_id)) {
address_copy(&Address_Cache[i].address, src);
Address_Cache[i].max_apdu = max_apdu;
found = true;
break;
}
}
/* add new device - but only if bind requested */
if (!found) {
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (!Address_Cache[i].valid && Address_Cache[i].bind_request) {
Address_Cache[i].valid = true;
Address_Cache[i].bind_request = false;
Address_Cache[i].device_id = device_id;
Address_Cache[i].max_apdu = max_apdu;
address_copy(&Address_Cache[i].address, src);
break;
}
}
}
return;
}
bool address_get_by_index(unsigned index,
uint32_t * device_id, unsigned *max_apdu, BACNET_ADDRESS * src)
{
bool found = false; /* return value */
if (index < MAX_ADDRESS_CACHE) {
if (Address_Cache[index].valid) {
address_copy(src, &Address_Cache[index].address);
*device_id = Address_Cache[index].device_id;
*max_apdu = Address_Cache[index].max_apdu;
found = true;
}
}
return found;
}
unsigned address_count(void)
{
unsigned i;
unsigned count = 0; /* return value */
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
if (Address_Cache[i].valid)
count++;
}
return count;
}
bool address_match(BACNET_ADDRESS * dest, BACNET_ADDRESS * src)
{
unsigned i;
unsigned max_len;
bool match = true; /* return value */
if (dest->mac_len != src->mac_len)
match = 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])
match = false;
}
if (dest->net != src->net)
match = false;
if (dest->len != src->len)
match = 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])
match = false;
}
return match;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
static void set_address(unsigned index, BACNET_ADDRESS * dest)
{
unsigned i;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = index;
}
dest->mac_len = MAX_MAC_LEN;
dest->net = 7;
dest->len = MAX_MAC_LEN;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = index;
}
}
void testAddress(Test * pTest)
{
unsigned i, count;
BACNET_ADDRESS src;
uint32_t device_id = 0;
unsigned max_apdu = 480;
BACNET_ADDRESS test_address;
uint32_t test_device_id = 0;
unsigned test_max_apdu = 0;
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
set_address(i, &src);
device_id = i * 255;
address_add(device_id, max_apdu, &src);
count = address_count();
ct_test(pTest, count == (i + 1));
}
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
device_id = i * 255;
ct_test(pTest, address_get_by_device(device_id, &test_max_apdu,
&test_address));
set_address(i, &src);
ct_test(pTest, test_max_apdu == max_apdu);
ct_test(pTest, address_match(&test_address, &src));
ct_test(pTest, address_get_by_index(i, &test_device_id,
&test_max_apdu, &test_address));
ct_test(pTest, test_device_id == device_id);
ct_test(pTest, test_max_apdu == max_apdu);
ct_test(pTest, address_match(&test_address, &src));
ct_test(pTest, address_count() == MAX_ADDRESS_CACHE);
}
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
device_id = i * 255;
address_remove_device(device_id);
ct_test(pTest, !address_get_by_device(device_id, &test_max_apdu,
&test_address));
count = address_count();
ct_test(pTest, count == (MAX_ADDRESS_CACHE - i - 1));
}
}
#ifdef TEST_ADDRESS
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Address", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAddress);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ADDRESS */
#endif /* TEST */
+74
View File
@@ -0,0 +1,74 @@
/*####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####*/
#ifndef ADDRESS_H
#define ADDRESS_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include "bacdef.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void address_init(void);
void address_copy(BACNET_ADDRESS * dest, BACNET_ADDRESS * src);
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);
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);
void address_add_binding(uint32_t device_id,
unsigned max_apdu, BACNET_ADDRESS * src);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+31
View File
@@ -0,0 +1,31 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_ADDRESS -g
SRCS = address.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = address
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+455
View File
@@ -0,0 +1,455 @@
/*####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 <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bits.h"
#include "apdu.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "tsm.h"
#include "dcc.h"
#include "iam.h"
/* a simple table for crossing the services supported */
static BACNET_SERVICES_SUPPORTED
confirmed_service_supported[MAX_BACNET_CONFIRMED_SERVICE] = {
SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM,
SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION,
SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION,
SERVICE_SUPPORTED_GET_ALARM_SUMMARY,
SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY,
SERVICE_SUPPORTED_SUBSCRIBE_COV,
SERVICE_SUPPORTED_ATOMIC_READ_FILE,
SERVICE_SUPPORTED_ATOMIC_WRITE_FILE,
SERVICE_SUPPORTED_ADD_LIST_ELEMENT,
SERVICE_SUPPORTED_REMOVE_LIST_ELEMENT,
SERVICE_SUPPORTED_CREATE_OBJECT,
SERVICE_SUPPORTED_DELETE_OBJECT,
SERVICE_SUPPORTED_READ_PROPERTY,
SERVICE_SUPPORTED_READ_PROPERTY_CONDITIONAL,
SERVICE_SUPPORTED_READ_PROPERTY_MULTIPLE,
SERVICE_SUPPORTED_WRITE_PROPERTY,
SERVICE_SUPPORTED_WRITE_PROPERTY_MULTIPLE,
SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL,
SERVICE_SUPPORTED_PRIVATE_TRANSFER,
SERVICE_SUPPORTED_TEXT_MESSAGE,
SERVICE_SUPPORTED_REINITIALIZE_DEVICE,
SERVICE_SUPPORTED_VT_OPEN,
SERVICE_SUPPORTED_VT_CLOSE,
SERVICE_SUPPORTED_VT_DATA,
SERVICE_SUPPORTED_AUTHENTICATE,
SERVICE_SUPPORTED_REQUEST_KEY,
SERVICE_SUPPORTED_READ_RANGE,
SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION,
SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY,
SERVICE_SUPPORTED_GET_EVENT_INFORMATION
};
/* a simple table for crossing the services supported */
static BACNET_SERVICES_SUPPORTED
unconfirmed_service_supported[MAX_BACNET_UNCONFIRMED_SERVICE] = {
SERVICE_SUPPORTED_I_AM,
SERVICE_SUPPORTED_I_HAVE,
SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION,
SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION,
SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER,
SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE,
SERVICE_SUPPORTED_TIME_SYNCHRONIZATION,
SERVICE_SUPPORTED_WHO_HAS,
SERVICE_SUPPORTED_WHO_IS,
SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION
};
/* Confirmed Function Handlers */
/* If they are not set, they are handled by a reject message */
static confirmed_function Confirmed_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_confirmed_handler(BACNET_CONFIRMED_SERVICE service_choice,
confirmed_function pFunction)
{
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
Confirmed_Function[service_choice] = pFunction;
}
/* Allow the APDU handler to automatically reject */
static confirmed_function Unrecognized_Service_Handler;
void apdu_set_unrecognized_service_handler_handler(confirmed_function
pFunction)
{
Unrecognized_Service_Handler = pFunction;
}
/* Unconfirmed Function Handlers */
/* If they are not set, they are not handled */
static unconfirmed_function
Unconfirmed_Function[MAX_BACNET_UNCONFIRMED_SERVICE] = {
NULL
};
void apdu_set_unconfirmed_handler(BACNET_UNCONFIRMED_SERVICE
service_choice, unconfirmed_function pFunction)
{
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE)
Unconfirmed_Function[service_choice] = pFunction;
}
bool apdu_service_supported(BACNET_SERVICES_SUPPORTED service_supported)
{
int i = 0;
bool status = false;
bool found = false;
if (service_supported < MAX_BACNET_SERVICES_SUPPORTED) {
/* is it a confirmed service? */
for (i = 0; i < MAX_BACNET_CONFIRMED_SERVICE; i++) {
if (confirmed_service_supported[i] == service_supported) {
if (Confirmed_Function[i] != NULL)
status = true;
found = true;
break;
}
}
if (!found) {
/* is it an unconfirmed service? */
for (i = 0; i < MAX_BACNET_UNCONFIRMED_SERVICE; i++) {
if (unconfirmed_service_supported[i] == service_supported) {
if (Unconfirmed_Function[i] != NULL)
status = true;
break;
}
}
}
}
return status;
}
/* Confirmed ACK Function Handlers */
static void *Confirmed_ACK_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_confirmed_simple_ack_handler(BACNET_CONFIRMED_SERVICE
service_choice, confirmed_simple_ack_function pFunction)
{
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
Confirmed_ACK_Function[service_choice] = (void *) pFunction;
break;
default:
break;
}
}
void apdu_set_confirmed_ack_handler(BACNET_CONFIRMED_SERVICE
service_choice, confirmed_ack_function pFunction)
{
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
Confirmed_ACK_Function[service_choice] = (void *) pFunction;
break;
default:
break;
}
}
static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE];
void apdu_set_error_handler(BACNET_CONFIRMED_SERVICE service_choice,
error_function pFunction)
{
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE)
Error_Function[service_choice] = pFunction;
}
static abort_function Abort_Function;
void apdu_set_abort_handler(abort_function pFunction)
{
Abort_Function = pFunction;
}
static reject_function Reject_Function;
void apdu_set_reject_handler(reject_function pFunction)
{
Reject_Function = pFunction;
}
uint16_t apdu_decode_confirmed_service_request(uint8_t * apdu, /* APDU data */
uint16_t apdu_len,
BACNET_CONFIRMED_SERVICE_DATA * service_data,
uint8_t * service_choice,
uint8_t ** service_request, uint16_t * service_request_len)
{
uint16_t len = 0; /* counts where we are in PDU */
service_data->segmented_message = (apdu[0] & BIT3) ? true : false;
service_data->more_follows = (apdu[0] & BIT2) ? true : false;
service_data->segmented_response_accepted =
(apdu[0] & BIT1) ? true : false;
service_data->max_segs = decode_max_segs(apdu[1]);
service_data->max_resp = decode_max_apdu(apdu[1]);
service_data->invoke_id = apdu[2];
len = 3;
if (service_data->segmented_message) {
service_data->sequence_number = apdu[len++];
service_data->proposed_window_number = apdu[len++];
}
*service_choice = apdu[len++];
*service_request = &apdu[len];
*service_request_len = apdu_len - len;
return len;
}
void apdu_handler(BACNET_ADDRESS * src, uint8_t * apdu, /* APDU data */
uint16_t apdu_len)
{
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 };
uint8_t invoke_id = 0;
uint8_t service_choice = 0;
uint8_t *service_request = NULL;
uint16_t service_request_len = 0;
uint16_t len = 0; /* counts where we are in PDU */
uint8_t tag_number = 0;
uint32_t len_value = 0;
int error_code = 0;
int error_class = 0;
uint8_t reason = 0;
bool server = false;
if (apdu) {
/* PDU Type */
switch (apdu[0] & 0xF0) {
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
len = apdu_decode_confirmed_service_request(&apdu[0], /* APDU data */
apdu_len,
&service_data,
&service_choice, &service_request, &service_request_len);
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated. */
if (dcc_communication_disabled() &&
((service_choice !=
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL)
&& (service_choice !=
SERVICE_CONFIRMED_REINITIALIZE_DEVICE)))
break;
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
(Confirmed_Function[service_choice]))
Confirmed_Function[service_choice] (service_request,
service_request_len, src, &service_data);
else if (Unrecognized_Service_Handler)
Unrecognized_Service_Handler(service_request,
service_request_len, src, &service_data);
break;
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
if (dcc_communication_disabled())
break;
service_choice = apdu[1];
service_request = &apdu[2];
service_request_len = apdu_len - 2;
if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) {
if (Unconfirmed_Function[service_choice])
Unconfirmed_Function[service_choice] (service_request,
service_request_len, src);
}
break;
case PDU_TYPE_SIMPLE_ACK:
invoke_id = apdu[1];
service_choice = apdu[2];
switch (service_choice) {
case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM:
case SERVICE_CONFIRMED_COV_NOTIFICATION:
case SERVICE_CONFIRMED_EVENT_NOTIFICATION:
case SERVICE_CONFIRMED_SUBSCRIBE_COV:
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY:
case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION:
/* Object Access Services */
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_DELETE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROPERTY:
case SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE:
/* Remote Device Management Services */
case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL:
case SERVICE_CONFIRMED_REINITIALIZE_DEVICE:
case SERVICE_CONFIRMED_TEXT_MESSAGE:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_CLOSE:
/* Security Services */
case SERVICE_CONFIRMED_REQUEST_KEY:
if (Confirmed_ACK_Function[service_choice]) {
((confirmed_simple_ack_function)
Confirmed_ACK_Function[service_choice]) (src,
invoke_id);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
break;
case PDU_TYPE_COMPLEX_ACK:
service_ack_data.segmented_message =
(apdu[0] & BIT3) ? true : false;
service_ack_data.more_follows =
(apdu[0] & BIT2) ? true : false;
invoke_id = service_ack_data.invoke_id = apdu[1];
len = 2;
if (service_ack_data.segmented_message) {
service_ack_data.sequence_number = apdu[len++];
service_ack_data.proposed_window_number = apdu[len++];
}
service_choice = apdu[len++];
service_request = &apdu[len];
service_request_len = apdu_len - len;
switch (service_choice) {
case SERVICE_CONFIRMED_GET_ALARM_SUMMARY:
case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY:
case SERVICE_CONFIRMED_GET_EVENT_INFORMATION:
/* File Access Services */
case SERVICE_CONFIRMED_ATOMIC_READ_FILE:
case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE:
/* Object Access Services */
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_READ_PROPERTY:
case SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL:
case SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE:
case SERVICE_CONFIRMED_READ_RANGE:
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
/* Virtual Terminal Services */
case SERVICE_CONFIRMED_VT_OPEN:
case SERVICE_CONFIRMED_VT_DATA:
/* Security Services */
case SERVICE_CONFIRMED_AUTHENTICATE:
if (Confirmed_ACK_Function[service_choice]) {
((confirmed_ack_function)
Confirmed_ACK_Function[service_choice])
(service_request, service_request_len, src,
&service_ack_data);
}
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
break;
case PDU_TYPE_SEGMENT_ACK:
/* FIXME: what about a denial of service attack here?
we could check src to see if that matched the tsm */
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ERROR:
invoke_id = apdu[1];
service_choice = apdu[2];
len = 3;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_class);
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
/* FIXME: we could validate that the tag is enumerated... */
len += decode_enumerated(&apdu[len], len_value, &error_code);
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
if (Error_Function[service_choice])
Error_Function[service_choice] (src,
invoke_id, error_class, error_code);
}
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_REJECT:
invoke_id = apdu[1];
reason = apdu[2];
if (Reject_Function)
Reject_Function(src, invoke_id, reason);
tsm_free_invoke_id(invoke_id);
break;
case PDU_TYPE_ABORT:
server = apdu[0] & 0x01;
invoke_id = apdu[1];
reason = apdu[2];
if (Abort_Function)
Abort_Function(src, invoke_id, reason, server);
tsm_free_invoke_id(invoke_id);
break;
default:
break;
}
}
return;
}
+158
View File
@@ -0,0 +1,158 @@
/*####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 APDU_H
#define APDU_H
#include <stdbool.h>
#include <stdint.h>
#include "bacdef.h"
#include "bacenum.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
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;
/* generic unconfirmed function handler */
/* Suitable to handle the following services: */
/* I_Am, Who_Is, Unconfirmed_COV_Notification, I_Have, */
/* Unconfirmed_Event_Notification, Unconfirmed_Private_Transfer, */
/* Unconfirmed_Text_Message, Time_Synchronization, Who_Has, */
/* UTC_Time_Synchronization */
typedef void (*unconfirmed_function) (uint8_t * service_request,
uint16_t len, BACNET_ADDRESS * src);
/* generic confirmed function handler */
/* Suitable to handle the following services: */
/* Acknowledge_Alarm, Confirmed_COV_Notification, */
/* Confirmed_Event_Notification, Get_Alarm_Summary, */
/* Get_Enrollment_Summary_Handler, Get_Event_Information, */
/* Subscribe_COV_Handler, Subscribe_COV_Property, */
/* Life_Safety_Operation, Atomic_Read_File, */
/* Confirmed_Atomic_Write_File, Add_List_Element, */
/* Remove_List_Element, Create_Object_Handler, */
/* Delete_Object_Handler, Read_Property, */
/* Read_Property_Conditional, Read_Property_Multiple, Read_Range, */
/* Write_Property, Write_Property_Multiple, */
/* Device_Communication_Control, Confirmed_Private_Transfer, */
/* Confirmed_Text_Message, Reinitialize_Device, */
/* VT_Open, VT_Close, VT_Data_Handler, */
/* Authenticate, Request_Key */
typedef void (*confirmed_function) (uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
/* generic confirmed simple ack function handler */
typedef void (*confirmed_simple_ack_function) (BACNET_ADDRESS * src,
uint8_t invoke_id);
/* generic confirmed ack function handler */
typedef void (*confirmed_ack_function) (uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
/* generic error reply function */
typedef void (*error_function) (BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code);
/* generic abort reply function */
typedef void (*abort_function) (BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t abort_reason, bool server);
/* generic reject reply function */
typedef void (*reject_function) (BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t reject_reason);
void apdu_set_confirmed_ack_handler(BACNET_CONFIRMED_SERVICE
service_choice, confirmed_ack_function pFunction);
void apdu_set_confirmed_simple_ack_handler(BACNET_CONFIRMED_SERVICE
service_choice, confirmed_simple_ack_function pFunction);
/* configure reject for confirmed services that are not supported */
void apdu_set_unrecognized_service_handler_handler(confirmed_function
pFunction);
void apdu_set_confirmed_handler(BACNET_CONFIRMED_SERVICE
service_choice, confirmed_function pFunction);
void apdu_set_unconfirmed_handler(BACNET_UNCONFIRMED_SERVICE
service_choice, unconfirmed_function pFunction);
/* returns true if the service is supported by a handler */
bool apdu_service_supported(BACNET_SERVICES_SUPPORTED
service_supported);
void apdu_set_error_handler(BACNET_CONFIRMED_SERVICE service_choice,
error_function pFunction);
void apdu_set_abort_handler(abort_function pFunction);
void apdu_set_reject_handler(reject_function pFunction);
uint16_t apdu_decode_confirmed_service_request(uint8_t * apdu, /* APDU data */
uint16_t apdu_len,
BACNET_CONFIRMED_SERVICE_DATA * service_data,
uint8_t * service_choice,
uint8_t ** service_request, uint16_t * service_request_len);
void apdu_handler(BACNET_ADDRESS * src, /* source address */
uint8_t * apdu, /* APDU data */
uint16_t pdu_len); /* for confirmed messages */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+75
View File
@@ -0,0 +1,75 @@
/*####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 ARCNET_H
#define ARCNET_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bacdef.h"
#include "npdu.h"
/* specific defines for ARCNET */
#define MAX_HEADER (1+1+2+2+1+1+1+1)
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
bool arcnet_valid(void);
void arcnet_cleanup(void);
bool arcnet_init(char *interface_name);
/* function to send a packet out the 802.2 socket */
/* returns zero on success, non-zero on failure */
int arcnet_send_pdu(BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len); /* number of bytes of data */
/* receives an framed packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t arcnet_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout); /* milliseconds to wait for a packet */
void arcnet_get_my_address(BACNET_ADDRESS * my_address);
void arcnet_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+473
View File
@@ -0,0 +1,473 @@
/*####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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "device.h"
#include "arf.h"
/* 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, Device_Max_APDU_Length_Accepted());
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_READ_FILE; /* service choice */
apdu_len = 4;
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_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;
int 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 = 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, Device_Max_APDU_Length_Accepted()); */
*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 */
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_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_unsigned(&apdu[apdu_len],
data->type.record.RecordCount);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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;
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 != 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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);
/* 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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;
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);
} else if (test_data.access == FILE_RECORD_ACCESS) {
ct_test(pTest, test_data.type.record.fileStartRecord ==
data->type.record.fileStartRecord);
ct_test(pTest, test_data.type.record.RecordCount ==
data->type.record.RecordCount);
}
ct_test(pTest, octetstring_length(&test_data.fileData) ==
octetstring_length(&data->fileData));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData),
octetstring_value(&data->fileData),
octetstring_length(&test_data.fileData)) == 0);
}
void testAtomicReadFileAck(Test * pTest)
{
BACNET_ATOMIC_READ_FILE_DATA data = { 0 };
uint8_t test_octet_string[32] = "Joshua-Mary-Anna-Christopher";
data.endOfFile = true;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = 0;
octetstring_init(&data.fileData,
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 = 2;
octetstring_init(&data.fileData,
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
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
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 */
+102
View File
@@ -0,0 +1,102 @@
/*####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 ATOMIC_READ_FILE_H
#define ATOMIC_READ_FILE_H
#include <stdint.h>
#include <stdbool.h>
#include "bacdcode.h"
#include "bacstr.h"
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;
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
+34
View File
@@ -0,0 +1,34 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
# -g for debugging with gdb
DEFINES = -DBACFILE=1 -DBACDL_BIP=1 -DTEST -DTEST_ATOMIC_READ_FILE
INCLUDES = -I. -Idemo/object -Itest
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
arf.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = atomicreadfile
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+405
View File
@@ -0,0 +1,405 @@
/*####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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "device.h"
#include "awf.h"
/* 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 */
if (apdu) {
apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
apdu[1] =
encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted());
apdu[2] = invoke_id;
apdu[3] = SERVICE_CONFIRMED_ATOMIC_WRITE_FILE; /* service choice */
apdu_len = 4;
apdu_len += encode_tagged_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_tagged_signed(&apdu[apdu_len],
data->type.stream.fileStartPosition);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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_tagged_signed(&apdu[apdu_len],
data->type.record.fileStartRecord);
apdu_len += encode_tagged_unsigned(&apdu[apdu_len],
data->type.record.returnedRecordCount);
apdu_len += encode_tagged_octet_string(&apdu[apdu_len],
&data->fileData);
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;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int 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 = 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, &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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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.returnedRecordCount);
/* 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;
len += decode_octet_string(&apdu[len],
len_value_type, &data->fileData);
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, Device_Max_APDU_Length_Accepted()); */
*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_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) ==
octetstring_length(&data->fileData));
ct_test(pTest, memcmp(octetstring_value(&test_data.fileData),
octetstring_value(&data->fileData),
octetstring_length(&test_data.fileData)) == 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,
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 = 2;
octetstring_init(&data.fileData,
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
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
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 */
+97
View File
@@ -0,0 +1,97 @@
/*####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 ATOMIC_WRITE_FILE_H
#define ATOMIC_WRITE_FILE_H
#include <stdint.h>
#include <stdbool.h>
#include "bacdcode.h"
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_ATOMIC_WRITE_FILE_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Atomic Write File */
/* encode service */
int awf_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA * data);
/* decode the service request only */
int awf_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA * data);
int awf_decode_apdu(uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA * data);
/* Atomic Write File Ack */
/* encode service */
int awf_ack_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA * data);
/* decode the service request only */
int awf_ack_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_ATOMIC_WRITE_FILE_DATA * data);
int awf_ack_decode_apdu(uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id, BACNET_ATOMIC_WRITE_FILE_DATA * data);
#ifdef TEST
#include "ctest.h"
void test_AtomicWriteFile(Test * pTest);
void test_AtomicWriteFileAck(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+34
View File
@@ -0,0 +1,34 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
DEFINES = -DBACFILE=1 -DBACDL_BIP=1 -DTEST -DTEST_ATOMIC_WRITE_FILE
INCLUDES = -I. -Idemo/object -Itest
CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
awf.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = atomicwritefile
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
/*####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 BACAPP_H
#define BACAPP_H
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "bacdef.h"
#include "bacstr.h"
#include "datetime.h"
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 */
bool Boolean;
uint32_t Unsigned_Int;
int32_t Signed_Int;
float Real;
double Double;
BACNET_OCTET_STRING Octet_String;
BACNET_CHARACTER_STRING Character_String;
BACNET_BIT_STRING Bit_String;
int Enumerated;
BACNET_DATE Date;
BACNET_TIME Time;
BACNET_OBJECT_ID Object_Id;
} type;
/* simple linked list if needed */
struct BACnet_Application_Data_Value *next;
} BACNET_APPLICATION_DATA_VALUE;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int bacapp_encode_data(uint8_t * apdu,
BACNET_APPLICATION_DATA_VALUE * value);
int bacapp_decode_application_data(uint8_t * apdu,
int max_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,
int 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, int max_apdu_len,
BACNET_PROPERTY_ID property);
#if PRINT_ENABLED
#define BACAPP_PRINT_ENABLED
#else
#ifdef TEST
#define BACAPP_PRINT_ENABLED
#endif
#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_APPLICATION_DATA_VALUE * value,
BACNET_PROPERTY_ID property);
#else
#define bacapp_parse_application_data(x,y,z) {(void)x;(void)y;(void)z;}
#define bacapp_print_value(x,y,z) {(void)x;(void)y;(void)z;}
#endif
#ifdef TEST
#include "ctest.h"
#include "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
Binary file not shown.
+36
View File
@@ -0,0 +1,36 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_BACNET_APPLICATION_DATA -g
SRCS = bacdcode.c \
bacstr.c \
bacapp.c \
datetime.c \
bactext.c \
indtext.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = bacapp
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
File diff suppressed because it is too large Load Diff
+213
View File
@@ -0,0 +1,213 @@
/*####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####*/
#ifndef BACDCODE_H
#define BACDCODE_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "bacdef.h"
#include "datetime.h"
#include "bacstr.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_and_value(uint8_t * apdu, uint8_t * tag_number,
uint32_t * value);
/* returns true if the tag is context specific */
bool decode_is_context_specific(uint8_t * apdu);
/* 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);
/* 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_tagged_null(uint8_t * apdu);
int encode_context_null(uint8_t * apdu, int tag_number);
/* from clause 20.2.3 Encoding of a Boolean Value */
int encode_tagged_boolean(uint8_t * apdu, bool boolean_value);
bool decode_boolean(uint32_t len_value);
int encode_context_boolean(uint8_t * apdu, int tag_number,
bool boolean_value);
bool decode_context_boolean(uint8_t * apdu);
/* 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);
/* returns the number of apdu bytes consumed */
int encode_bitstring(uint8_t * apdu, BACNET_BIT_STRING * bit_string);
int encode_tagged_bitstring(uint8_t * apdu,
BACNET_BIT_STRING * bit_string);
int encode_context_bitstring(uint8_t * apdu, int 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 decode_real(uint8_t * apdu, float *real_value);
int encode_bacnet_real(float value, uint8_t * apdu);
int encode_tagged_real(uint8_t * apdu, float value);
int encode_context_real(uint8_t * apdu, int tag_number, float 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, int *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, int tag_number,
int object_type, uint32_t instance);
int encode_tagged_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_tagged_octet_string(uint8_t * apdu,
BACNET_OCTET_STRING * octet_string);
int encode_context_octet_string(uint8_t * apdu,
int tag_number, BACNET_OCTET_STRING * octet_string);
int decode_octet_string(uint8_t * apdu, uint32_t len_value,
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 */
int encode_bacnet_character_string(uint8_t * apdu,
BACNET_CHARACTER_STRING * char_string);
int encode_tagged_character_string(uint8_t * apdu,
BACNET_CHARACTER_STRING * char_string);
int encode_context_character_string(uint8_t * apdu, int tag_number,
BACNET_CHARACTER_STRING * char_string);
int decode_character_string(uint8_t * apdu, uint32_t len_value,
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, int tag_number,
uint32_t value);
int encode_tagged_unsigned(uint8_t * apdu, uint32_t value);
int decode_unsigned(uint8_t * apdu, uint32_t len_value,
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_tagged_signed(uint8_t * apdu, int32_t value);
int encode_context_signed(uint8_t * apdu, int tag_number,
int32_t value);
int decode_signed(uint8_t * apdu, uint32_t len_value, 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, int *value);
int encode_bacnet_enumerated(uint8_t * apdu, int value);
int encode_tagged_enumerated(uint8_t * apdu, int value);
int encode_context_enumerated(uint8_t * apdu, int tag_number,
int 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_tagged_time(uint8_t * apdu, BACNET_TIME * btime);
int decode_bacnet_time(uint8_t * apdu, BACNET_TIME * btime);
int encode_context_time(uint8_t * apdu, int 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_tagged_date(uint8_t * apdu, BACNET_DATE * bdate);
int encode_context_date(uint8_t * apdu, int tag_number,
BACNET_DATE * bdate);
int decode_date(uint8_t * apdu, BACNET_DATE * bdate);
/* two octet unsigned16 */
int encode_unsigned16(uint8_t * apdu, uint16_t value);
int decode_unsigned16(uint8_t * apdu, uint16_t * value);
/* four octet unsigned32 */
int encode_unsigned32(uint8_t * apdu, uint32_t value);
int decode_unsigned32(uint8_t * apdu, uint32_t * value);
/* 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);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
Binary file not shown.
+30
View File
@@ -0,0 +1,30 @@
#Makefile to build unit tests
CC = gcc
BASEDIR = .
CFLAGS = -Wall -I. -Itest -g -DTEST -DTEST_DECODE
TARGET = bacdcode
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
all: ${TARGET}
${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 ${OBJS} ${TARGET} *.bak
include: .depend
+99
View File
@@ -0,0 +1,99 @@
/*####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 BACDEF_H
#define BACDEF_H
#include <stddef.h>
#include <stdint.h>
#include "bacenum.h"
#include "config.h"
/* This stack implements this version of BACnet */
#define BACNET_PROTOCOL_VERSION 1
#define BACNET_PROTOCOL_REVISION 5
/* 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 */
#define BACNET_ARRAY_ALL (~0)
/* Priority Array for commandable objects */
#define BACNET_NO_PRIORITY 0
#define BACNET_MIN_PRIORITY 1
#define BACNET_MAX_PRIORITY 16
/* embedded systems need fixed name sizes */
#define MAX_OBJECT_NAME 10
/* common object properties */
typedef struct BACnet_Object_Data {
uint32_t Object_Identifier;
char Object_Name[MAX_OBJECT_NAME];
BACNET_OBJECT_TYPE Object_Type;
} BACNET_OBJECT_DATA;
#define BACNET_BROADCAST_NETWORK 0xFFFF
/* IPv6 (16 octets) coupled with port number (2 octets) */
#define MAX_MAC_LEN 18
struct BACnet_Device_Address {
/* mac_len = 0 if global address */
int 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 */
int len; /* length of MAC address */
uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */
};
typedef struct BACnet_Device_Address BACNET_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)
#endif
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
/*####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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
/* 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_tagged_enumerated(&apdu[apdu_len], error_class);
apdu_len += encode_tagged_enumerated(&apdu[apdu_len], error_code);
}
return apdu_len;
}
/* 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;
int 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 = 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 = 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 = 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;
}
#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 < MAX_BACNET_ERROR_CLASS; error_class++) {
for (error_code = 0;
error_code < MAX_BACNET_ERROR_CODE; 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 = LAST_PROPRIETARY_ERROR_CLASS;
error_code = LAST_PROPRIETARY_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);
}
#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 */
+74
View File
@@ -0,0 +1,74 @@
/*####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 BACERROR_H
#define BACERROR_H
#include <stdint.h>
#include <stdbool.h>
#include "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
+34
View File
@@ -0,0 +1,34 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_BACERROR -g
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
bacerror.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = bacerror
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+111
View File
@@ -0,0 +1,111 @@
/*####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>
#include <stdio.h>
#include "bacprop.h"
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:
fprintf(stderr, "Unsupported object type");
break;
}
return -1;
}
+58
View File
@@ -0,0 +1,58 @@
/*####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####*/
#ifndef BACPROP_H
#define BACPROP_H
#include <stdbool.h>
#include <stdint.h>
#include "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
+637
View File
@@ -0,0 +1,637 @@
/*####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####*/
#include <stdbool.h>
#include <stdint.h>
#include <string.h> /* for strlen */
#include "bacstr.h"
#include "bits.h"
void bitstring_init(BACNET_BIT_STRING * bit_string)
{
int i;
bit_string->bits_used = 0;
for (i = 0; i < MAX_BITSTRING_BYTES; i++) {
bit_string->value[i] = 0;
}
}
void bitstring_set_bit(BACNET_BIT_STRING * bit_string, uint8_t bit,
bool value)
{
uint8_t byte_number = bit / 8;
uint8_t bit_mask = 1;
if (byte_number < MAX_BITSTRING_BYTES) {
/* set max bits used */
if (bit_string->bits_used < (bit + 1))
bit_string->bits_used = bit + 1;
bit_mask = bit_mask << (bit - (byte_number * 8));
if (value)
bit_string->value[byte_number] |= bit_mask;
else
bit_string->value[byte_number] &= (~(bit_mask));
}
}
bool bitstring_bit(BACNET_BIT_STRING * bit_string, uint8_t bit)
{
bool value = false;
uint8_t byte_number = bit / 8;
uint8_t bit_mask = 1;
if (bit < (MAX_BITSTRING_BYTES * 8)) {
bit_mask = bit_mask << (bit - (byte_number * 8));
if (bit_string->value[byte_number] & bit_mask)
value = true;
}
return value;
}
uint8_t bitstring_bits_used(BACNET_BIT_STRING * bit_string)
{
return bit_string->bits_used;
}
/* returns the number of bytes that a bit string is using */
int bitstring_bytes_used(BACNET_BIT_STRING * bit_string)
{
int len = 0; /* return value */
uint8_t used_bytes = 0;
uint8_t last_bit = 0;
if (bit_string->bits_used) {
last_bit = bit_string->bits_used - 1;
used_bytes = last_bit / 8;
/* add one for the first byte */
used_bytes++;
len = used_bytes;
}
return len;
}
uint8_t bitstring_octet(BACNET_BIT_STRING * bit_string, uint8_t index)
{
uint8_t octet = 0;
if (bit_string) {
if (index < MAX_BITSTRING_BYTES) {
octet = bit_string->value[index];
}
}
return octet;
}
bool bitstring_set_octet(BACNET_BIT_STRING * bit_string,
uint8_t index, uint8_t octet)
{
bool status = false;
if (bit_string) {
if (index < MAX_BITSTRING_BYTES) {
bit_string->value[index] = octet;
status = true;
}
}
return status;
}
bool bitstring_set_bits_used(BACNET_BIT_STRING * bit_string,
uint8_t bytes_used, uint8_t unused_bits)
{
bool status = false;
if (bit_string) {
/* FIXME: check that bytes_used is at least one? */
bit_string->bits_used = bytes_used * 8;
bit_string->bits_used -= unused_bits;
status = true;
}
return status;
}
uint8_t bitstring_bits_capacity(BACNET_BIT_STRING * bit_string)
{
if (bit_string)
return (sizeof(bit_string->value) * 8);
else
return 0;
}
#define CHARACTER_STRING_CAPACITY (MAX_CHARACTER_STRING_BYTES - 1)
/* returns false if the string exceeds capacity
initialize by using length=0 */
bool characterstring_init(BACNET_CHARACTER_STRING * char_string,
uint8_t encoding, char *value, size_t length)
{
bool status = false; /* return value */
size_t i; /* counter */
if (char_string) {
char_string->length = 0;
char_string->encoding = encoding;
/* save a byte at the end for NULL -
note: assumes printable characters */
if (length <= CHARACTER_STRING_CAPACITY) {
if (value) {
for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) {
if (i < length) {
char_string->value[char_string->length] = value[i];
char_string->length++;
} else
char_string->value[i] = 0;
}
} else {
for (i = 0; i < MAX_CHARACTER_STRING_BYTES; i++) {
char_string->value[i] = 0;
}
}
status = true;
}
}
return status;
}
bool characterstring_init_ansi(BACNET_CHARACTER_STRING * char_string,
char *value)
{
return characterstring_init(char_string,
CHARACTER_ANSI_X34, value, value ? strlen(value) : 0);
}
bool characterstring_copy(BACNET_CHARACTER_STRING * dest,
BACNET_CHARACTER_STRING * src)
{
return characterstring_init(dest,
characterstring_encoding(src),
characterstring_value(src), characterstring_length(src));
}
bool characterstring_same(BACNET_CHARACTER_STRING * dest,
BACNET_CHARACTER_STRING * src)
{
size_t i; /* counter */
bool same_status = false;
if (src && dest) {
if ((src->length == dest->length) &&
(src->encoding == dest->encoding)) {
same_status = true;
for (i = 0; i < src->length; i++) {
if (src->value[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
} else if (src) {
if (src->length == 0)
same_status = true;
} else if (dest) {
if (dest->length == 0)
same_status = true;
}
return same_status;
}
bool characterstring_ansi_same(BACNET_CHARACTER_STRING * dest,
const char *src)
{
size_t i; /* counter */
bool same_status = false;
if (src && dest) {
if ((dest->length == strlen(src)) &&
(dest->encoding == CHARACTER_ANSI_X34)) {
same_status = true;
for (i = 0; i < dest->length; i++) {
if (src[i] != dest->value[i]) {
same_status = false;
break;
}
}
}
}
/* NULL matches an empty string in our world */
else if (src) {
if (strlen(src) == 0)
same_status = true;
} else if (dest) {
if (dest->length == 0)
same_status = true;
}
return same_status;
}
/* returns false if the string exceeds capacity */
bool characterstring_append(BACNET_CHARACTER_STRING * char_string,
char *value, size_t length)
{
size_t i; /* counter */
bool status = false; /* return value */
if (char_string) {
if ((length + char_string->length) <= CHARACTER_STRING_CAPACITY) {
for (i = 0; i < length; i++) {
char_string->value[char_string->length] = value[i];
char_string->length++;
}
status = true;
}
}
return status;
}
/* 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 status = false; /* return value */
if (char_string) {
if (length <= CHARACTER_STRING_CAPACITY) {
char_string->length = length;
status = true;
}
}
return status;
}
/* Returns the value. */
char *characterstring_value(BACNET_CHARACTER_STRING * char_string)
{
char *value = NULL;
if (char_string) {
value = char_string->value;
}
return value;
}
/* returns the length. */
size_t characterstring_length(BACNET_CHARACTER_STRING * char_string)
{
size_t length = 0;
if (char_string) {
/* FIXME: validate length is within bounds? */
length = char_string->length;
}
return length;
}
size_t characterstring_capacity(BACNET_CHARACTER_STRING * char_string)
{
size_t length = 0;
if (char_string) {
length = CHARACTER_STRING_CAPACITY;
}
return length;
}
/* returns the encoding. */
uint8_t characterstring_encoding(BACNET_CHARACTER_STRING * char_string)
{
uint8_t encoding = 0;
if (char_string) {
encoding = char_string->encoding;
}
return encoding;
}
/* 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)
{
bool status = false; /* return value */
size_t i; /* counter */
if (octet_string) {
octet_string->length = 0;
if (length <= sizeof(octet_string->value)) {
if (value) {
for (i = 0; i < length; i++) {
octet_string->value[octet_string->length] = value[i];
octet_string->length++;
}
} else {
for (i = 0; i < sizeof(octet_string->value); i++) {
octet_string->value[i] = 0;
}
}
status = true;
}
}
return status;
}
bool octetstring_copy(BACNET_OCTET_STRING * dest,
BACNET_OCTET_STRING * src)
{
return octetstring_init(dest,
octetstring_value(src), octetstring_length(src));
}
/* returns false if the string exceeds capacity */
bool octetstring_append(BACNET_OCTET_STRING * octet_string,
uint8_t * value, size_t length)
{
size_t i; /* counter */
bool status = false; /* return value */
if (octet_string) {
if ((length + octet_string->length) <= sizeof(octet_string->value)) {
for (i = 0; i < length; i++) {
octet_string->value[octet_string->length] = value[i];
octet_string->length++;
}
status = true;
}
}
return status;
}
/* 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)
{
bool status = false; /* return value */
if (octet_string) {
if (length <= sizeof(octet_string->value)) {
octet_string->length = length;
status = true;
}
}
return status;
}
/* returns the length. Returns the value in parameter. */
uint8_t *octetstring_value(BACNET_OCTET_STRING * octet_string)
{
uint8_t *value = NULL;
if (octet_string) {
value = octet_string->value;
}
return value;
}
/* returns the length. */
size_t octetstring_length(BACNET_OCTET_STRING * octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = octet_string->length;
}
return length;
}
/* returns the length. */
size_t octetstring_capacity(BACNET_OCTET_STRING * octet_string)
{
size_t length = 0;
if (octet_string) {
/* FIXME: validate length is within bounds? */
length = sizeof(octet_string->value);
}
return length;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBitString(Test * pTest)
{
uint8_t bit = 0;
BACNET_BIT_STRING bit_string;
bitstring_init(&bit_string);
/* verify initialization */
ct_test(pTest, bitstring_bits_used(&bit_string) == 0);
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
ct_test(pTest, bitstring_bit(&bit_string, bit) == false);
}
/* test for true */
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
bitstring_set_bit(&bit_string, bit, true);
ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1));
ct_test(pTest, bitstring_bit(&bit_string, bit) == true);
}
/* test for false */
bitstring_init(&bit_string);
for (bit = 0; bit < (MAX_BITSTRING_BYTES * 8); bit++) {
bitstring_set_bit(&bit_string, bit, false);
ct_test(pTest, bitstring_bits_used(&bit_string) == (bit + 1));
ct_test(pTest, bitstring_bit(&bit_string, bit) == false);
}
}
void testCharacterString(Test * pTest)
{
BACNET_CHARACTER_STRING bacnet_string;
char *value = "Joshua,Mary,Anna,Christopher";
char test_value[MAX_APDU] = "Patricia";
char test_append_value[MAX_APDU] = " and the Kids";
char test_append_string[MAX_APDU] = "";
bool status = false;
size_t length = 0;
size_t test_length = 0;
size_t i = 0;
/* verify initialization */
status =
characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, 0);
ct_test(pTest, status == true);
ct_test(pTest, characterstring_length(&bacnet_string) == 0);
ct_test(pTest,
characterstring_encoding(&bacnet_string) == CHARACTER_ANSI_X34);
/* bounds check */
status = characterstring_init(&bacnet_string,
CHARACTER_ANSI_X34,
NULL, characterstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
characterstring_truncate(&bacnet_string,
characterstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
characterstring_truncate(&bacnet_string,
characterstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
test_length = strlen(test_value);
status = characterstring_init(&bacnet_string,
CHARACTER_ANSI_X34, &test_value[0], test_length);
ct_test(pTest, status == true);
value = characterstring_value(&bacnet_string);
length = characterstring_length(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_value[i]);
}
test_length = strlen(test_append_value);
status = characterstring_append(&bacnet_string,
&test_append_value[0], test_length);
strcat(test_append_string, test_value);
strcat(test_append_string, test_append_value);
test_length = strlen(test_append_string);
ct_test(pTest, status == true);
length = characterstring_length(&bacnet_string);
value = characterstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_append_string[i]);
}
}
void testOctetString(Test * pTest)
{
BACNET_OCTET_STRING bacnet_string;
uint8_t *value = NULL;
uint8_t test_value[MAX_APDU] = "Patricia";
uint8_t test_append_value[MAX_APDU] = " and the Kids";
uint8_t test_append_string[MAX_APDU] = "";
bool status = false;
size_t length = 0;
size_t test_length = 0;
size_t i = 0;
/* verify initialization */
status = octetstring_init(&bacnet_string, NULL, 0);
ct_test(pTest, status == true);
ct_test(pTest, octetstring_length(&bacnet_string) == 0);
value = octetstring_value(&bacnet_string);
for (i = 0; i < octetstring_capacity(&bacnet_string); i++) {
ct_test(pTest, value[i] == 0);
}
/* bounds check */
status = octetstring_init(&bacnet_string, NULL,
octetstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status = octetstring_init(&bacnet_string, NULL,
octetstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
status =
octetstring_truncate(&bacnet_string,
octetstring_capacity(&bacnet_string) + 1);
ct_test(pTest, status == false);
status =
octetstring_truncate(&bacnet_string,
octetstring_capacity(&bacnet_string));
ct_test(pTest, status == true);
test_length = strlen((char *) test_value);
status = octetstring_init(&bacnet_string, &test_value[0], test_length);
ct_test(pTest, status == true);
length = octetstring_length(&bacnet_string);
value = octetstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_value[i]);
}
test_length = strlen((char *) test_append_value);
status = octetstring_append(&bacnet_string,
&test_append_value[0], test_length);
strcat((char *) test_append_string, (char *) test_value);
strcat((char *) test_append_string, (char *) test_append_value);
test_length = strlen((char *) test_append_string);
ct_test(pTest, status == true);
length = octetstring_length(&bacnet_string);
value = octetstring_value(&bacnet_string);
ct_test(pTest, length == test_length);
for (i = 0; i < test_length; i++) {
ct_test(pTest, value[i] == test_append_string[i]);
}
}
#ifdef TEST_BACSTR
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Strings", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBitString);
assert(rc);
rc = ct_addTestFunction(pTest, testCharacterString);
assert(rc);
rc = ct_addTestFunction(pTest, testOctetString);
assert(rc);
/* configure output */
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_BACSTR */
#endif /* TEST */
+141
View File
@@ -0,0 +1,141 @@
/*####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 BACSTR_H
#define BACSTR_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "bacdef.h"
/* bit strings
They could be as large as 256/8=32 octets */
#define MAX_BITSTRING_BYTES 15
typedef struct BACnet_Bit_String {
uint8_t bits_used;
uint8_t value[MAX_BITSTRING_BYTES];
} BACNET_BIT_STRING;
#define MAX_CHARACTER_STRING_BYTES (MAX_APDU-6)
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_APDU - 6];
} 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,
bool value);
bool bitstring_bit(BACNET_BIT_STRING * bit_string, uint8_t bit);
uint8_t bitstring_bits_used(BACNET_BIT_STRING * bit_string);
/* returns the number of bytes that a bit string is using */
int 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);
/* returns false if the string exceeds capacity
initialize by using length=0 */
bool characterstring_init(BACNET_CHARACTER_STRING * char_string,
uint8_t encoding, char *value, size_t length);
/* used for ANSI C-Strings */
bool characterstring_init_ansi(BACNET_CHARACTER_STRING * char_string,
char *value);
bool characterstring_copy(BACNET_CHARACTER_STRING * dest,
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,
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);
/* 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);
bool octetstring_copy(BACNET_OCTET_STRING * dest,
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);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+28
View File
@@ -0,0 +1,28 @@
#Makefile to build unit tests
CC = gcc
BASEDIR = .
CFLAGS = -Wall -I. -Itest -g -DTEST -DTEST_BACSTR
TARGET = bacstr
SRCS = bacstr.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
all: ${TARGET}
${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 ${OBJS} ${TARGET} *.bak
include: .depend
File diff suppressed because it is too large Load Diff
+81
View File
@@ -0,0 +1,81 @@
/*####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 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
#ifdef BACTEXT_PRINT_ENABLED
#include <stdbool.h>
#include <stdint.h>
#include "indtext.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
const char *bactext_confirmed_service_name(int index);
const char *bactext_unconfirmed_service_name(int index);
const char *bactext_application_tag_name(int index);
const char *bactext_object_type_name(int index);
const char *bactext_property_name(int index);
const char *bactext_engineering_unit_name(int index);
const char *bactext_reject_reason_name(int index);
const char *bactext_abort_reason_name(int index);
const char *bactext_error_class_name(int index);
const char *bactext_error_code_name(int index);
unsigned bactext_property_id(const char *name);
const char *bactext_month_name(int index);
const char *bactext_week_of_month_name(int index);
const char *bactext_day_of_week_name(int index);
const char *bactext_event_state_name(int index);
const char *bactext_binary_present_value_name(int index);
const char *bactext_reliability_name(int index);
const char *bactext_device_status_name(int index);
const char *bactext_segmentation_name(int index);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BACTEXT_PRINT_ENABLED */
#endif
+28
View File
@@ -0,0 +1,28 @@
/* Big-Endian systems save the most significant byte first. */
/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */
/* "Network Byte Order" is also know as "Big-Endian Byte Order" */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x04 */
/* x[1] = 0x03 */
/* x[2] = 0x02 */
/* x[3] = 0x01 */
/* Little-Endian systems save the least significant byte first. */
/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x01 */
/* x[1] = 0x02 */
/* x[2] = 0x03 */
/* x[3] = 0x04 */
int big_endian(void)
{
union {
long l;
char c[sizeof(long)];
} u;
u.l = 1;
return (u.c[sizeof(long) - 1] == 1);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef BIGEND_H
#define BIGEND_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Big-Endian systems save the most significant byte first. */
/* Sun and Motorola processors, IBM-370s and PDP-10s are big-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x04 */
/* x[1] = 0x03 */
/* x[2] = 0x02 */
/* x[3] = 0x01 */
/* Little-Endian systems save the least significant byte first. */
/* The entire Intel x86 family, Vaxes, Alphas and PDP-11s are little-endian. */
/* for example, a 4 byte integer 67305985 is 0x04030201 in hexidecimal. */
/* x[0] = 0x01 */
/* x[1] = 0x02 */
/* x[2] = 0x03 */
/* x[3] = 0x04 */
int big_endian(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+282
View File
@@ -0,0 +1,282 @@
/*####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> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bip.h"
#include "net.h" /* custom per port */
static int BIP_Socket = -1;
/* port to use - stored in host byte order */
static uint16_t BIP_Port = 0xBAC0;
/* IP Address - stored in host byte order */
static struct in_addr BIP_Address;
/* Broadcast Address - stored in host byte order */
static struct in_addr BIP_Broadcast_Address;
void bip_set_socket(int sock_fd)
{
BIP_Socket = sock_fd;
}
int bip_socket(void)
{
return BIP_Socket;
}
bool bip_valid(void)
{
return (BIP_Socket != -1);
}
void bip_cleanup(void)
{
if (bip_valid())
close(BIP_Socket);
BIP_Socket = -1;
return;
}
/* set using network byte order */
void bip_set_addr(uint32_t net_address)
{
BIP_Address.s_addr = ntohl(net_address);
}
/* returns host byte order */
uint32_t bip_get_addr(void)
{
return BIP_Address.s_addr;
}
/* set using network byte order */
void bip_set_broadcast_addr(uint32_t net_address)
{
BIP_Broadcast_Address.s_addr = ntohl(net_address);
}
/* returns host byte order */
uint32_t bip_get_broadcast_addr(void)
{
return BIP_Broadcast_Address.s_addr;
}
/* set using host byte order */
void bip_set_port(uint16_t port)
{
BIP_Port = port;
}
/* returns host byte order */
uint16_t bip_get_port(void)
{
return BIP_Port;
}
/* function to send a packet out the BACnet/IP socket (Annex J) */
/* returns number of bytes sent on success, negative number on failure */
int bip_send_pdu(BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len)
{ /* number of bytes of data */
struct sockaddr_in bip_dest;
uint8_t mtu[MAX_MPDU] = { 0 };
int mtu_len = 0;
int bytes_sent = 0;
(void) npdu_data;
/* assumes that the driver has already been initialized */
if (BIP_Socket < 0)
return BIP_Socket;
mtu[0] = BVLL_TYPE_BACNET_IP;
bip_dest.sin_family = AF_INET;
if (dest->mac_len == 6) {
(void) decode_unsigned32(&dest->mac[0],
&(bip_dest.sin_addr.s_addr));
(void) decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
}
/* broadcast */
else if (dest->mac_len == 0) {
bip_dest.sin_addr.s_addr = htonl(BIP_Broadcast_Address.s_addr);
bip_dest.sin_port = htons(BIP_Port);
memset(&(bip_dest.sin_zero), '\0', 8);
mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
} else
return -1;
mtu_len = 2;
mtu_len +=
encode_unsigned16(&mtu[mtu_len],
(uint16_t) (pdu_len + 4 /*inclusive */ ));
memcpy(&mtu[mtu_len], pdu, pdu_len);
mtu_len += pdu_len;
/* Send the packet */
bytes_sent = sendto(BIP_Socket, (char *) mtu, mtu_len, 0,
(struct sockaddr *) &bip_dest, sizeof(struct sockaddr));
return bytes_sent;
}
/* receives a BACnet/IP packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t bip_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout)
{ /* number of milliseconds to wait for a packet */
int received_bytes;
uint8_t buf[MAX_MPDU] = { 0 }; /* data */
uint16_t pdu_len = 0; /* return value */
fd_set read_fds;
int max;
struct timeval select_timeout;
struct sockaddr_in sin = { -1 };
socklen_t sin_len = sizeof(sin);
/* Make sure the socket is open */
if (BIP_Socket < 0)
return 0;
/* we could just use a non-blocking socket, but that consumes all
the CPU time. We can use a timeout; it is only supported as
a select. */
if (timeout >= 1000) {
select_timeout.tv_sec = timeout / 1000;
select_timeout.tv_usec =
1000 * (timeout - select_timeout.tv_sec * 1000);
} else {
select_timeout.tv_sec = 0;
select_timeout.tv_usec = 1000 * timeout;
}
FD_ZERO(&read_fds);
FD_SET((unsigned int) BIP_Socket, &read_fds);
max = BIP_Socket;
/* see if there is a packet for us */
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
received_bytes = recvfrom(BIP_Socket,
(char *) &buf[0], MAX_MPDU, 0,
(struct sockaddr *) &sin, &sin_len);
else
return 0;
/* See if there is a problem */
if (received_bytes < 0) {
return 0;
}
/* no problem, just no bytes */
if (received_bytes == 0)
return 0;
/* the signature of a BACnet/IP packet */
if (buf[0] != BVLL_TYPE_BACNET_IP)
return 0;
if ((buf[1] == BVLC_ORIGINAL_UNICAST_NPDU) ||
(buf[1] == BVLC_ORIGINAL_BROADCAST_NPDU)) {
/* ignore messages from me */
if (sin.sin_addr.s_addr == BIP_Address.s_addr)
pdu_len = 0;
else {
/* copy the source address
FIXME: IPv6? */
src->mac_len = 6;
(void) encode_unsigned32(&src->mac[0], sin.sin_addr.s_addr);
(void) encode_unsigned16(&src->mac[4], sin.sin_port);
/* FIXME: check destination address */
/* see if it is broadcast or for us */
/* decode the length of the PDU - length is inclusive of BVLC */
(void) decode_unsigned16(&buf[2], &pdu_len);
/* copy the buffer into the PDU */
pdu_len -= 4; /* BVLC header */
if (pdu_len < max_pdu)
memmove(&pdu[0], &buf[4], pdu_len);
/* ignore packets that are too large */
/* clients should check my max-apdu first */
else
pdu_len = 0;
}
}
#ifdef BBMD_ENABLED
if (buf[1] < MAX_BVLC_FUNCTION) {
bbmd_handler(&buf[0], received_bytes, &sin);
}
#endif
return pdu_len;
}
void bip_get_my_address(BACNET_ADDRESS * my_address)
{
int i = 0;
my_address->mac_len = 6;
(void) encode_unsigned32(&my_address->mac[0],
htonl(BIP_Address.s_addr));
(void) encode_unsigned16(&my_address->mac[4], htons(BIP_Port));
my_address->net = 0; /* local only, no routing */
my_address->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
my_address->adr[i] = 0;
}
return;
}
void bip_get_broadcast_address(BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
dest->mac_len = 6;
(void) encode_unsigned32(&dest->mac[0],
htonl(BIP_Broadcast_Address.s_addr));
(void) encode_unsigned16(&dest->mac[4], htons(BIP_Port));
dest->net = BACNET_BROADCAST_NETWORK;
dest->len = 0; /* no SLEN */
for (i = 0; i < MAX_MAC_LEN; i++) {
/* no SADR */
dest->adr[i] = 0;
}
}
return;
}
+99
View File
@@ -0,0 +1,99 @@
/*####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 BIP_H
#define BIP_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "bacdef.h"
#include "npdu.h"
#include "net.h"
/* specific defines for BACnet/IP over Ethernet */
#define MAX_HEADER (1 + 1 + 2)
#define MAX_MPDU (MAX_HEADER+MAX_PDU)
#define BVLL_TYPE_BACNET_IP (0x81)
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* note: define init and cleanup in your ports section */
bool bip_init(void);
/* normal functions... */
void bip_cleanup(void);
void bip_set_socket(int sock_fd);
int bip_socket(void);
bool bip_valid(void);
void bip_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */
void bip_get_my_address(BACNET_ADDRESS * my_address);
/* function to send a packet out the BACnet/IP socket */
/* returns zero on success, non-zero on failure */
int bip_send_pdu(BACNET_ADDRESS * dest, /* destination address */
BACNET_NPDU_DATA * npdu_data, /* network information */
uint8_t * pdu, /* any data to be sent - may be null */
unsigned pdu_len); /* number of bytes of data */
/* receives a BACnet/IP packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t bip_receive(BACNET_ADDRESS * src, /* source address */
uint8_t * pdu, /* PDU data */
uint16_t max_pdu, /* amount of space available in the PDU */
unsigned timeout); /* milliseconds to wait for a packet */
/* use host byte order for setting */
void bip_set_port(uint16_t port);
/* returns host byte order */
uint16_t bip_get_port(void);
/* use network byte order for setting */
void bip_set_addr(uint32_t net_address);
/* returns host byte order */
uint32_t bip_get_addr(void);
/* use network byte order for setting */
void bip_set_broadcast_addr(uint32_t net_address);
/* returns host byte order */
uint32_t bip_get_broadcast_addr(void);
void bip_set_interface(char *ifname);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+73
View File
@@ -0,0 +1,73 @@
/*####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####*/
#ifndef BITS_H
#define BITS_H
/********************************************************************
* Bit Masks
*********************************************************************/
#define BIT0 (0x01)
#define BIT1 (0x02)
#define BIT2 (0x04)
#define BIT3 (0x08)
#define BIT4 (0x10)
#define BIT5 (0x20)
#define BIT6 (0x40)
#define BIT7 (0x80)
#define BIT8 (0x0100)
#define BIT9 (0x0200)
#define BIT10 (0x0400)
#define BIT11 (0x0800)
#define BIT12 (0x1000)
#define BIT13 (0x2000)
#define BIT14 (0x4000)
#define BIT15 (0x8000)
#define BIT16 (0x010000UL)
#define BIT17 (0x020000UL)
#define BIT18 (0x040000UL)
#define BIT19 (0x080000UL)
#define BIT20 (0x100000UL)
#define BIT21 (0x200000UL)
#define BIT22 (0x400000UL)
#define BIT23 (0x800000UL)
#define BIT24 (0x01000000UL)
#define BIT25 (0x02000000UL)
#define BIT26 (0x04000000UL)
#define BIT27 (0x08000000UL)
#define BIT28 (0x10000000UL)
#define BIT29 (0x20000000UL)
#define BIT30 (0x40000000UL)
#define BIT31 (0x80000000UL)
#endif
+624
View File
@@ -0,0 +1,624 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include <time.h> /* for the standard bool type. */
#include "bacdcode.h"
#include "bip.h"
#include "net.h" /* custom per port */
/* Handle the BACnet Virtual Link Control (BVLC), which includes:
BACnet Broadcast Management Device,
Broadcast Distribution Table, and
Foreign Device Registration */
typedef struct
{
/* true if valid entry - false if not */
bool valid;
/* BACnet/IP address */
struct in_addr dest_address;
uint16_t dest_port;
/* Broadcast Distribution Mask - stored in host byte order */
struct in_addr broadcast_mask;
} BBMD_TABLE_ENTRY;
#define MAX_BBMD_ENTRIES 128
static BBMD_TABLE_ENTRY BBMD_Table[MAX_BBMD_ENTRIES];
/*Each device that registers as a foreign device shall be placed
in an entry in the BBMD's Foreign Device Table (FDT). Each
entry shall consist of the 6-octet B/IP address of the registrant;
the 2-octet Time-to-Live value supplied at the time of
registration; and a 2-octet value representing the number of
seconds remaining before the BBMD will purge the registrant's FDT
entry if no re-registration occurs. This value will be initialized
to the 2-octet Time-to-Live value supplied at the time of
registration.*/
typedef struct
{
bool valid;
/* BACnet/IP address */
struct in_addr dest_address;
uint16_t dest_port;
/* seconds for valid entry lifetime */
uint16_t time_to_live;
time_t seconds_remaining; /* includes 30 second grace period */
} FD_TABLE_ENTRY;
#define MAX_FD_ENTRIES 128
static FD_TABLE_ENTRY FD_Table[MAX_FD_ENTRIES];
void bvlc_maintenance_timer(unsigned seconds)
{
unsigned i = 0;
for (i = 0; i < MAX_FD_ENTRIES; i++) {
if (FD_Table[i].valid)
{
if (FD_Table[i].seconds_remaining)
{
if (FD_Table[i].seconds_remaining < seconds)
FD_Table[i].seconds_remaining = 0;
else
FD_Table[i].seconds_remaining -= seconds;
if (FD_Table[i].seconds_remaining == 0)
{
FD_Table[i].valid = false;
}
}
}
}
}
int bvlc_encode_bip_address(
uint8_t * pdu,
struct in_addr *address, /* in host format */
uint16_t port)
{
int len = 0;
if (pdu) {
len = encode_unsigned32(&pdu[0], address->s_addr);
len += encode_unsigned16(&pdu[len], port);
}
return len;
}
int bvlc_decode_bip_address(
uint8_t * pdu,
struct in_addr *address, /* in host format */
uint16_t * port)
{
}
/* used for both read and write entries */
int bvlc_encode_address_entry(uint8_t * pdu,
struct in_addr *address,
uint16_t port,
struct in_addr *mask)
{
int len = 0;
if (pdu) {
len = bvlc_encode_bip_address(pdu, address, port);
len += encode_unsigned32(&pdu[len], mask->s_addr);
}
return len;
}
int bvlc_encode_bvlc_result(uint8_t * pdu,
BACNET_BVLC_RESULT result_code)
{
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_RESULT;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 6);
encode_unsigned16(&pdu[4], result_code);
}
return 6;
}
int bvlc_encode_write_bdt_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_read_bdt(
uint8_t * pdu)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_BROADCAST_DISTRIBUTION_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4);
len = 4;
}
return len;
}
int bvlc_encode_read_bdt_ack_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_forwarded_npdu(uint8_t * pdu,
BACNET_ADDRESS * src,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0;
unsigned i; /* for loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_FORWARDED_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + 6 + npdu_length);
len = 4;
for (i = 0; i < 6; i++) {
pdu[len] = src->adr[i];
len++;
}
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_register_foreign_device(uint8_t * pdu,
uint16_t time_to_live_seconds)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_REGISTER_FOREIGN_DEVICE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 6);
encode_unsigned16(&pdu[2], time_to_live_seconds);
len = 6;
}
return len;
}
int bvlc_encode_read_fdt(
uint8_t * pdu)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4);
len = 4;
}
return len;
}
int bvlc_encode_read_fdt_ack_init(
uint8_t * pdu,
unsigned entries)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE_ACK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 4 + entries * 10);
len = 4;
}
return len;
}
int bvlc_encode_delete_fdt_entry(uint8_t * pdu,
struct in_addr *address,
uint16_t port)
{
int len = 0;
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_READ_FOREIGN_DEVICE_TABLE;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
encode_unsigned16(&pdu[2], 10);
/* FDT Entry */
encode_unsigned32(&pdu[0], address->s_addr);
encode_unsigned16(&pdu[4], port);
len = 10;
}
return len;
}
int bvlc_encode_distribute_broadcast_to_network(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i; /* for loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_original_unicast_npdu(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i = 0; /* loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
int bvlc_encode_original_broadcast_npdu(uint8_t * pdu,
uint8_t * npdu,
unsigned npdu_length)
{
int len = 0; /* return value */
unsigned i = 0; /* loop counter */
if (pdu) {
pdu[0] = BVLL_TYPE_BACNET_IP;
pdu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
/* The 2-octet BVLC Length field is the length, in octets,
of the entire BVLL message, including the two octets of the
length field itself, most significant octet first. */
len = encode_unsigned16(&pdu[2], 4 + npdu_length) + 2;
for (i = 0; i < npdu_length; i++) {
pdu[len] = npdu[i];
len++;
}
}
return len;
}
/* copy the source internet address to the BACnet address */
/* FIXME: IPv6? */
/* FIXME: is sockaddr_in host or network order? */
void bvlc_internet_to_bacnet_address(
BACNET_ADDRESS * src, /* returns the BACnet source address */
struct sockaddr_in *sin)
{ /* source internet address */
int len = 0;
if (src && sin)
{
len = encode_unsigned32(&src->mac[0], sin->sin_addr.s_addr);
len += encode_unsigned16(&src->mac[4], sin->sin_port);
src->mac_len = len;
src->net = 0;
src->len = 0;
}
return;
}
/* copy the source internet address to the BACnet address */
/* FIXME: IPv6? */
+98
View File
@@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!DOCTYPE CodeBlocks_project_file>
<CodeBlocks_project_file>
<FileVersion major="1" minor="1"/>
<Project>
<Option title="BVLC Unit Test"/>
<Option makefile="Makefile"/>
<Option makefile_is_custom="0"/>
<Option compiler="2"/>
<Build>
<Target title="default">
<Option output="C:\code\bacnet-stack\bbmd.exe"/>
<Option working_dir="."/>
<Option object_output=".objs"/>
<Option deps_output=".deps"/>
<Option type="1"/>
<Option compiler="2"/>
<Option projectResourceIncludeDirsRelation="1"/>
</Target>
</Build>
<Compiler>
<Add option="-w-aus"/>
<Add option="-w-sig"/>
<Add option="-DTEST;TEST_BBMD;BACDL_BIP=1;TSM_ENABLED=1"/>
<Add directory="."/>
<Add directory="demo\handler"/>
<Add directory="demo\object"/>
<Add directory="ports\win32"/>
<Add directory="test"/>
</Compiler>
<Linker>
<Add library="..\..\BCC55\Lib\import32.lib"/>
<Add library="..\..\BCC55\Lib\cw32mt.lib"/>
</Linker>
<Unit filename="bacdcode.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bacdcode.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacdef.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacenum.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacstr.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bacstr.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bigend.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bigend.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bvlc.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bvlc.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="test\ctest.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="test\ctest.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
</Project>
</CodeBlocks_project_file>
+52
View File
@@ -0,0 +1,52 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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 BVLC_H
#define BVLC_H
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bip.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* called from BACnet/IP handler */
void bvlc_handler(uint8_t * buf, int len, struct sockaddr_in *sin);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /*
+70
View File
@@ -0,0 +1,70 @@
/* Defines the bit/byte/word/long conversions that are used in code */
#ifndef BYTES_H
#define BYTES_H
#include <stdint.h>
#ifndef LO_NIB
#define LO_NIB(b) ((b) & 0xF)
#endif
#ifndef HI_NIB
#define HI_NIB(b) ((b) >> 4)
#endif
#ifndef LO_BYTE
#define LO_BYTE(w) ((uint8_t)(w))
#endif
#ifndef HI_BYTE
#define HI_BYTE(w) ((uint8_t)((uint16_t)(w) >> 8))
#endif
#ifndef LO_WORD
#define LO_WORD(x) ((uint16_t)(x))
#endif
#ifndef HI_WORD
#define HI_WORD(x) ((uint16_t)((uint32_t)(x) >> 16))
#endif
#ifndef MAKE_WORD
#define MAKE_WORD(lo,hi) \
((uint16_t)(((uint8_t)(lo))|(((uint16_t)((uint8_t)(hi)))<<8)))
#endif
#ifndef MAKE_LONG
#define MAKE_LONG(lo,hi) \
((uint32_t)(((uint16_t)(lo))|(((uint32_t)((uint16_t)(hi)))<<16)))
#endif
#endif /* end of header file */
+16
View File
@@ -0,0 +1,16 @@
#!/bin/sh
directory=${1-`pwd`}
for filename in $( find $directory -name '*.c' )
do
echo Converting $filename
ccmtcnvt $filename > /tmp/ccmtcnvt.karg
mv /tmp/ccmtcnvt.karg $filename
done
for filename in $( find $directory -name '*.h' )
do
echo Converting $filename
ccmtcnvt $filename > /tmp/ccmtcnvt.karg
mv /tmp/ccmtcnvt.karg $filename
done
+33
View File
@@ -0,0 +1,33 @@
#ifndef CONFIG_H
#define CONFIG_H
/* declare a single physical layer */
/*#include "bip.h" */
/*#include "ethernet.h" */
/*#include "arcnet.h" */
/*#include "mstp.h" */
/* Max number of bytes in an APDU. */
/* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */
/* This is used in constructing messages and to tell others our limits */
/* 50 is the minimum; adjust to your memory and physical layer constraints */
/* Lon=206, MS/TP=480, ARCNET=480, Ethernet=1476 */
#define MAX_APDU 50
/* #define MAX_APDU 480 */
/* #define MAX_APDU 1476 */
/* for confirmed messages, this is the number of transactions */
/* that we hold in a queue waiting for timeout. */
/* Configure to zero if you don't want any confirmed messages */
/* Configure from 1..255 for number of outstanding confirmed */
/* requests available. */
#define MAX_TSM_TRANSACTIONS 255
/* The address cache is used for binding to BACnet devices */
/* The number of entries corresponds to the number of */
/* devices that might respond to an I-Am on the network. */
/* If your device is a simple server and does not need to bind, */
/* then you don't need to use this. */
#define MAX_ADDRESS_CACHE 255
#endif
+970
View File
@@ -0,0 +1,970 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "bacapp.h"
#include "cov.h"
#include "device.h"
#include "datalink.h"
#include "npdu.h"
/* encode service */
/* Change-Of-Value Services
COV Subscribe
COV Subscribe Property
COV Notification
Unconfirmed COV Notification
*/
static int notify_encode_adpu(uint8_t * apdu, BACNET_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
if (apdu) {
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - initiatingDeviceIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1, OBJECT_DEVICE, data->initiatingDeviceIdentifier);
apdu_len += len;
/* tag 2 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
2,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
/* tag 3 - timeRemaining */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->timeRemaining);
apdu_len += len;
/* tag 4 - listOfValues */
len = encode_opening_tag(&apdu[apdu_len], 4);
apdu_len += len;
/* the first value includes a pointer to the next value, etc */
/* FIXME: for small implementations, we might try a partial
approach like the rpm.c where the values are encoded with
a separate function */
value = &data->listOfValues;
while (value != NULL) {
/* tag 0 - propertyIdentifier */
len = encode_context_enumerated(&apdu[apdu_len],
0, value->propertyIdentifier);
apdu_len += len;
/* tag 1 - propertyArrayIndex OPTIONAL */
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len],
1, value->propertyArrayIndex);
apdu_len += len;
}
/* tag 2 - value */
/* abstract syntax gets enclosed in a context tag */
len = encode_opening_tag(&apdu[apdu_len], 2);
apdu_len += len;
len = bacapp_encode_application_data(&apdu[apdu_len],
&value->value);
apdu_len += len;
len = encode_closing_tag(&apdu[apdu_len], 2);
apdu_len += len;
/* tag 3 - priority OPTIONAL */
if (value->priority != BACNET_NO_PRIORITY) {
len = encode_context_unsigned(&apdu[apdu_len], 3,
value->priority);
apdu_len += len;
}
/* is there another one to encode? */
/* FIXME: check to see if there is room in the APDU */
value = value->next;
}
len = encode_closing_tag(&apdu[apdu_len], 4);
apdu_len += len;
}
return apdu_len;
}
int ccov_notify_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
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_COV_NOTIFICATION;
apdu_len = 4;
len = notify_encode_adpu(&apdu[apdu_len], data);
apdu_len += len;
}
return apdu_len;
}
int ucov_notify_encode_apdu(uint8_t * apdu, BACNET_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
if (apdu && data) {
apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST;
apdu[1] = SERVICE_UNCONFIRMED_COV_NOTIFICATION; /* service choice */
apdu_len = 2;
len = notify_encode_adpu(&apdu[apdu_len], data);
apdu_len += len;
}
return apdu_len;
}
/* decode the service request only */
/* COV and Unconfirmed COV are the same */
int cov_notify_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
int property = 0; /* for decoding */
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - initiatingDeviceIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->initiatingDeviceIdentifier);
if (decoded_type != OBJECT_DEVICE)
return -1;
} else
return -1;
/* tag 2 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 2)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -1;
/* tag 3 - timeRemaining */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->timeRemaining = decoded_value;
} else
return -1;
/* tag 4: opening context tag - listOfValues */
if (!decode_is_opening_tag_number(&apdu[len], 4))
return -1;
/* a tag number of 4 is not extended so only one octet */
len++;
/* the first value includes a pointer to the next value, etc */
value = &data->listOfValues;
while (value != NULL) {
/* tag 0 - propertyIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
value->propertyIdentifier = property;
} else
return -1;
/* tag 1 - propertyArrayIndex OPTIONAL */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_unsigned(&apdu[len], len_value, &decoded_value);
value->propertyArrayIndex = decoded_value;
} else
value->propertyArrayIndex = BACNET_ARRAY_ALL;
/* tag 2: opening context tag - value */
if (!decode_is_opening_tag_number(&apdu[len], 2))
return -1;
/* a tag number of 2 is not extended so only one octet */
len++;
len += bacapp_decode_application_data(&apdu[len],
apdu_len - len, &value->value);
/* FIXME: check the return value; abort if no valid data? */
/* FIXME: there might be more than one data element in here! */
if (!decode_is_closing_tag_number(&apdu[len], 2))
return -1;
/* a tag number of 2 is not extended so only one octet */
len++;
/* tag 3 - priority OPTIONAL */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_unsigned(&apdu[len], len_value, &decoded_value);
value->priority = decoded_value;
} else
value->priority = BACNET_NO_PRIORITY;
/* end of list? */
if (decode_is_closing_tag_number(&apdu[len], 4))
break;
/* is there another one to decode? */
value = value->next;
/* out of room to store more values */
if (value == NULL)
return -1;
}
}
return len;
}
/*
12.11.38Active_COV_Subscriptions
The Active_COV_Subscriptions property is a List of BACnetCOVSubscription, each of which consists of a Recipient, a
Monitored Property Reference, an Issue Confirmed Notifications flag, a Time Remaining value and an optional COV
Increment. This property provides a network-visible indication of those COV subscriptions that are active at any given time.
Whenever a COV Subscription is created with the SubscribeCOV or SubscribeCOVProperty service, a new entry is added to
the Active_COV_Subscriptions list. Similarly, whenever a COV Subscription is terminated, the corresponding entry shall be
removed from the Active_COV_Subscriptions list.
*/
/*
SubscribeCOV-Request ::= SEQUENCE {
subscriberProcessIdentifier [0] Unsigned32,
monitoredObjectIdentifier [1] BACnetObjectIdentifier,
issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
lifetime [3] Unsigned OPTIONAL
}
*/
int cov_subscribe_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
if (apdu && data) {
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_SUBSCRIBE_COV;
apdu_len = 4;
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
/*
If both the 'Issue Confirmed Notifications' and
'Lifetime' parameters are absent, then this shall
indicate a cancellation request.
*/
if (!data->cancellationRequest) {
/* tag 2 - issueConfirmedNotifications */
len = encode_context_boolean(&apdu[apdu_len],
2, data->issueConfirmedNotifications);
apdu_len += len;
/* tag 3 - lifetime */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->lifetime);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int cov_subscribe_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -1;
/* tag 2 - issueConfirmedNotifications - optional */
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
data->issueConfirmedNotifications =
decode_context_boolean(&apdu[len]);
len += len_value;
} else
data->cancellationRequest = true;
/* tag 3 - lifetime - optional */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->lifetime = decoded_value;
} else
data->lifetime = 0;
}
return len;
}
/*
SubscribeCOVProperty-Request ::= SEQUENCE {
subscriberProcessIdentifier [0] Unsigned32,
monitoredObjectIdentifier [1] BACnetObjectIdentifier,
issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
lifetime [3] Unsigned OPTIONAL,
monitoredPropertyIdentifier [4] BACnetPropertyReference,
covIncrement [5] REAL OPTIONAL
}
BACnetPropertyReference ::= SEQUENCE {
propertyIdentifier [0] BACnetPropertyIdentifier,
propertyArrayIndex [1] Unsigned OPTIONAL
-- used only with array datatype
-- if omitted with an array the entire array is referenced
}
*/
int cov_subscribe_property_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* length of each encoding */
int apdu_len = 0; /* total length of the apdu, return value */
uint16_t max_apdu = Device_Max_APDU_Length_Accepted();
if (apdu && data) {
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_SUBSCRIBE_COV_PROPERTY;
apdu_len = 4;
/* tag 0 - subscriberProcessIdentifier */
len = encode_context_unsigned(&apdu[apdu_len],
0, data->subscriberProcessIdentifier);
apdu_len += len;
/* tag 1 - monitoredObjectIdentifier */
len = encode_context_object_id(&apdu[apdu_len],
1,
data->monitoredObjectIdentifier.type,
data->monitoredObjectIdentifier.instance);
apdu_len += len;
if (!data->cancellationRequest) {
/* tag 2 - issueConfirmedNotifications */
len = encode_context_boolean(&apdu[apdu_len],
2, data->issueConfirmedNotifications);
apdu_len += len;
/* tag 3 - lifetime */
len = encode_context_unsigned(&apdu[apdu_len],
3, data->lifetime);
apdu_len += len;
}
/* tag 4 - monitoredPropertyIdentifier */
len = encode_opening_tag(&apdu[apdu_len], 4);
apdu_len += len;
len = encode_context_enumerated(&apdu[apdu_len],
0, data->monitoredProperty.propertyIdentifier);
apdu_len += len;
if (data->monitoredProperty.propertyArrayIndex != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(&apdu[apdu_len],
1, data->monitoredProperty.propertyArrayIndex);
apdu_len += len;
}
len = encode_closing_tag(&apdu[apdu_len], 4);
apdu_len += len;
/* tag 5 - covIncrement */
if (data->covIncrementPresent) {
len = encode_context_real(&apdu[apdu_len],
5, data->covIncrement);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int cov_subscribe_property_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data)
{
int len = 0; /* return value */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t decoded_value = 0; /* for decoding */
int decoded_type = 0; /* for decoding */
int property = 0; /* for decoding */
if (apdu_len && data) {
/* tag 0 - subscriberProcessIdentifier */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->subscriberProcessIdentifier = decoded_value;
} else
return -1;
/* tag 1 - monitoredObjectIdentifier */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len +=
decode_object_id(&apdu[len], &decoded_type,
&data->monitoredObjectIdentifier.instance);
data->monitoredObjectIdentifier.type = decoded_type;
} else
return -2;
/* tag 2 - issueConfirmedNotifications - optional */
if (decode_is_context_tag(&apdu[len], 2)) {
data->cancellationRequest = false;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
data->issueConfirmedNotifications =
decode_context_boolean(&apdu[len]);
len++;
} else
data->cancellationRequest = true;
/* tag 3 - lifetime - optional */
if (decode_is_context_tag(&apdu[len], 3)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->lifetime = decoded_value;
} else
data->lifetime = 0;
/* tag 4 - monitoredPropertyIdentifier */
if (!decode_is_opening_tag_number(&apdu[len], 4))
return -3;
/* a tag number of 4 is not extended so only one octet */
len++;
/* the propertyIdentifier is tag 0 */
if (decode_is_context_tag(&apdu[len], 0)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_enumerated(&apdu[len], len_value, &property);
data->monitoredProperty.propertyIdentifier = property;
} else
return -4;
/* the optional array index is tag 1 */
if (decode_is_context_tag(&apdu[len], 1)) {
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
data->monitoredProperty.propertyArrayIndex = decoded_value;
} else {
data->monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL;
}
if (!decode_is_closing_tag_number(&apdu[len], 4))
return -5;
/* a tag number of 4 is not extended so only one octet */
len++;
/* tag 5 - covIncrement - optional */
if (decode_is_context_tag(&apdu[len], 5)) {
data->covIncrementPresent = true;
len +=
decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value);
len += decode_real(&apdu[len], &data->covIncrement);
} else
data->covIncrementPresent = false;
}
return len;
}
int ucov_notify_send(uint8_t * buffer, BACNET_COV_DATA * data)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
/* unconfirmed is a broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&buffer[0], &dest, NULL, &npdu_data);
/* encode the APDU portion of the packet */
len = ucov_notify_encode_apdu(&buffer[pdu_len], data);
pdu_len += len;
/* send the data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len);
return bytes_sent;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bacapp.h"
int ccov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id, BACNET_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_COV_NOTIFICATION)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_notify_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int ucov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_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_UNCONFIRMED_SERVICE_REQUEST)
return -2;
if (apdu[1] != SERVICE_UNCONFIRMED_COV_NOTIFICATION)
return -3;
/* optional limits - must be used as a pair */
offset = 2;
if (apdu_len > offset) {
len =
cov_notify_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int cov_subscribe_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id,
BACNET_SUBSCRIBE_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_subscribe_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
int cov_subscribe_property_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id,
BACNET_SUBSCRIBE_COV_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 -2;
/* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY)
return -3;
offset = 4;
/* optional limits - must be used as a pair */
if (apdu_len > offset) {
len =
cov_subscribe_property_decode_service_request(&apdu[offset],
apdu_len - offset, data);
}
return len;
}
/* dummy function stubs */
int npdu_encode_pdu(uint8_t * npdu,
BACNET_ADDRESS * dest,
BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data)
{
return 0;
}
void npdu_encode_npdu_data(BACNET_NPDU_DATA * npdu,
bool data_expecting_reply, BACNET_MESSAGE_PRIORITY priority)
{
}
/* dummy function stubs */
int datalink_send_pdu(BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data, uint8_t * pdu, unsigned pdu_len)
{
(void) dest;
(void) npdu_data;
(void) pdu;
(void) pdu_len;
return 0;
}
/* dummy function stubs */
void datalink_get_broadcast_address(BACNET_ADDRESS * dest)
{
(void) dest;
}
/* dummy function stubs */
uint16_t Device_Max_APDU_Length_Accepted(void)
{
return MAX_APDU;
}
void testCOVNotifyData(Test * pTest,
BACNET_COV_DATA * data, BACNET_COV_DATA * test_data)
{
ct_test(pTest,
test_data->subscriberProcessIdentifier ==
data->subscriberProcessIdentifier);
ct_test(pTest,
test_data->initiatingDeviceIdentifier ==
data->initiatingDeviceIdentifier);
ct_test(pTest,
test_data->monitoredObjectIdentifier.type ==
data->monitoredObjectIdentifier.type);
ct_test(pTest,
test_data->monitoredObjectIdentifier.instance ==
data->monitoredObjectIdentifier.instance);
ct_test(pTest, test_data->timeRemaining == data->timeRemaining);
/* FIXME: test the listOfValues in some clever manner */
}
void testUCOVNotifyData(Test * pTest, BACNET_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
len = ucov_notify_encode_apdu(&apdu[0], data);
ct_test(pTest, len > 0);
apdu_len = len;
test_data.listOfValues.next = NULL;
len = ucov_notify_decode_apdu(&apdu[0], apdu_len, &test_data);
ct_test(pTest, len != -1);
testCOVNotifyData(pTest, data, &test_data);
}
void testCCOVNotifyData(Test * pTest, uint8_t invoke_id,
BACNET_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = ccov_notify_encode_apdu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
test_data.listOfValues.next = NULL;
len = ccov_notify_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVNotifyData(pTest, data, &test_data);
}
void testCOVNotify(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_COV_DATA data;
/* BACNET_PROPERTY_VALUE value2; */
data.subscriberProcessIdentifier = 1;
data.initiatingDeviceIdentifier = 123;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.timeRemaining = 456;
data.listOfValues.propertyIdentifier = PROP_PRESENT_VALUE;
data.listOfValues.propertyArrayIndex = BACNET_ARRAY_ALL;
bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL,
"21.0", &data.listOfValues.value);
data.listOfValues.priority = 0;
data.listOfValues.next = NULL;
testUCOVNotifyData(pTest, &data);
testCCOVNotifyData(pTest, invoke_id, &data);
/* FIXME: add more values to the list of values */
}
void testCOVSubscribeData(Test * pTest,
BACNET_SUBSCRIBE_COV_DATA * data,
BACNET_SUBSCRIBE_COV_DATA * test_data)
{
ct_test(pTest,
test_data->subscriberProcessIdentifier ==
data->subscriberProcessIdentifier);
ct_test(pTest,
test_data->monitoredObjectIdentifier.type ==
data->monitoredObjectIdentifier.type);
ct_test(pTest,
test_data->monitoredObjectIdentifier.instance ==
data->monitoredObjectIdentifier.instance);
ct_test(pTest,
test_data->cancellationRequest == data->cancellationRequest);
if (!test_data->cancellationRequest) {
ct_test(pTest,
test_data->issueConfirmedNotifications ==
data->issueConfirmedNotifications);
ct_test(pTest, test_data->lifetime == data->lifetime);
}
}
void testCOVSubscribePropertyData(Test * pTest,
BACNET_SUBSCRIBE_COV_DATA * data,
BACNET_SUBSCRIBE_COV_DATA * test_data)
{
testCOVSubscribeData(pTest, data, test_data);
ct_test(pTest,
test_data->monitoredProperty.propertyIdentifier ==
data->monitoredProperty.propertyIdentifier);
ct_test(pTest,
test_data->monitoredProperty.propertyArrayIndex ==
data->monitoredProperty.propertyArrayIndex);
ct_test(pTest,
test_data->covIncrementPresent == data->covIncrementPresent);
if (test_data->covIncrementPresent) {
ct_test(pTest, test_data->covIncrement == data->covIncrement);
}
}
void testCOVSubscribeEncoding(Test * pTest, uint8_t invoke_id,
BACNET_SUBSCRIBE_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_SUBSCRIBE_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = cov_subscribe_encode_adpu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = cov_subscribe_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVSubscribeData(pTest, data, &test_data);
}
void testCOVSubscribePropertyEncoding(Test * pTest, uint8_t invoke_id,
BACNET_SUBSCRIBE_COV_DATA * data)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
BACNET_SUBSCRIBE_COV_DATA test_data;
uint8_t test_invoke_id = 0;
len = cov_subscribe_property_encode_adpu(&apdu[0], invoke_id, data);
ct_test(pTest, len != 0);
apdu_len = len;
len = cov_subscribe_property_decode_apdu(&apdu[0], apdu_len,
&test_invoke_id, &test_data);
ct_test(pTest, len > 0);
ct_test(pTest, test_invoke_id == invoke_id);
testCOVSubscribePropertyData(pTest, data, &test_data);
}
void testCOVSubscribe(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_SUBSCRIBE_COV_DATA data;
data.subscriberProcessIdentifier = 1;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.cancellationRequest = false;
data.issueConfirmedNotifications = true;
data.lifetime = 456;
testCOVSubscribeEncoding(pTest, invoke_id, &data);
data.cancellationRequest = true;
testCOVSubscribeEncoding(pTest, invoke_id, &data);
}
void testCOVSubscribeProperty(Test * pTest)
{
uint8_t invoke_id = 12;
BACNET_SUBSCRIBE_COV_DATA data;
data.subscriberProcessIdentifier = 1;
data.monitoredObjectIdentifier.type = OBJECT_ANALOG_INPUT;
data.monitoredObjectIdentifier.instance = 321;
data.cancellationRequest = false;
data.issueConfirmedNotifications = true;
data.lifetime = 456;
data.monitoredProperty.propertyIdentifier = PROP_FILE_SIZE;
data.monitoredProperty.propertyArrayIndex = BACNET_ARRAY_ALL;
data.covIncrementPresent = true;
data.covIncrement = 1.0;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
data.cancellationRequest = true;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
data.cancellationRequest = false;
data.covIncrementPresent = false;
testCOVSubscribePropertyEncoding(pTest, invoke_id, &data);
}
#ifdef TEST_COV
int main(int argc, char *argv[])
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet COV", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCOVNotify);
assert(rc);
rc = ct_addTestFunction(pTest, testCOVSubscribe);
assert(rc);
rc = ct_addTestFunction(pTest, testCOVSubscribeProperty);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_COV */
#endif /* TEST */
+80
View File
@@ -0,0 +1,80 @@
<?xml version="1.0"?>
<!DOCTYPE CodeBlocks_project_file>
<CodeBlocks_project_file>
<FileVersion major="1" minor="1"/>
<Project>
<Option title="COV Unit Test"/>
<Option makefile="Makefile"/>
<Option makefile_is_custom="0"/>
<Option compiler="0"/>
<Build>
<Target title="default">
<Option output="C:\code\bacnet-stack\cov.exe"/>
<Option working_dir="."/>
<Option object_output=".objs"/>
<Option deps_output=".deps"/>
<Option type="1"/>
<Option compiler="0"/>
<Option projectResourceIncludeDirsRelation="1"/>
</Target>
</Build>
<Compiler>
<Add option="-DTEST"/>
<Add option="-DTEST_WHOIS"/>
</Compiler>
<Unit filename="bacapp.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bacapp.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacdcode.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bacdcode.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacdef.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacenum.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="bacstr.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="bigend.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="cov.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
<Unit filename="cov.h">
<Option compilerVar=""/>
<Option compile="0"/>
<Option link="0"/>
<Option target="default"/>
</Unit>
<Unit filename="test\ctest.c">
<Option compilerVar="CC"/>
<Option target="default"/>
</Unit>
</Project>
</CodeBlocks_project_file>
+120
View File
@@ -0,0 +1,120 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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 COV_H
#define COV_H
#include <stdint.h>
#include <stdbool.h>
#include "bacapp.h"
struct BACnet_Property_Value;
typedef struct BACnet_Property_Value {
BACNET_PROPERTY_ID propertyIdentifier;
unsigned propertyArrayIndex;
BACNET_APPLICATION_DATA_VALUE value;
uint8_t priority;
/* simple linked list */
struct BACnet_Property_Value *next;
} BACNET_PROPERTY_VALUE;
typedef struct BACnet_COV_Data {
uint32_t subscriberProcessIdentifier;
uint32_t initiatingDeviceIdentifier;
BACNET_OBJECT_ID monitoredObjectIdentifier;
unsigned timeRemaining;
/* simple linked list of values */
BACNET_PROPERTY_VALUE listOfValues;
} BACNET_COV_DATA;
typedef struct BACnet_Property_Reference {
BACNET_PROPERTY_ID propertyIdentifier;
unsigned propertyArrayIndex; /* optional */
} BACNET_PROPERTY_REFERENCE;
typedef struct BACnet_Subscribe_COV_Data {
uint32_t subscriberProcessIdentifier;
BACNET_OBJECT_ID monitoredObjectIdentifier;
bool cancellationRequest; /* true if this is a cancellation request */
bool issueConfirmedNotifications; /* optional */
unsigned lifetime; /* optional */
BACNET_PROPERTY_REFERENCE monitoredProperty;
bool covIncrementPresent; /* true if present */
float covIncrement; /* optional */
} BACNET_SUBSCRIBE_COV_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
int ucov_notify_encode_apdu(uint8_t * apdu, BACNET_COV_DATA * data);
int ucov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_DATA * data);
int ucov_notify_send(uint8_t * buffer, BACNET_COV_DATA * data);
int ccov_notify_encode_apdu(uint8_t * apdu,
uint8_t invoke_id, BACNET_COV_DATA * data);
int ccov_notify_decode_apdu(uint8_t * apdu,
unsigned apdu_len, uint8_t * invoke_id, BACNET_COV_DATA * data);
/* common for both confirmed and unconfirmed */
int cov_notify_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_COV_DATA * data);
int cov_subscribe_property_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data);
int cov_subscribe_property_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data);
int cov_subscribe_decode_service_request(uint8_t * apdu,
unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA * data);
int cov_subscribe_encode_adpu(uint8_t * apdu,
uint8_t invoke_id, BACNET_SUBSCRIBE_COV_DATA * data);
#ifdef TEST
#include "ctest.h"
void testCOVNotify(Test * pTest);
void testCOVSubscribeProperty(Test * pTest);
void testCOVSubscribe(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+37
View File
@@ -0,0 +1,37 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -Idemo/object -DTEST -DTEST_COV -DBACDL_TEST=1 -DBIG_ENDIAN=0 -g
SRCS = bacdcode.c \
bacstr.c \
datetime.c \
bacapp.c \
indtext.c \
bactext.c \
cov.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = cov
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+153
View File
@@ -0,0 +1,153 @@
/*####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####*/
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
/* Accumulate "dataValue" into the CRC in crcValue. */
/* Return value is updated CRC */
/* */
/* The ^ operator means exclusive OR. */
/* Note: This function is copied directly from the BACnet standard. */
uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue)
{
uint16_t crc;
crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
/* Exclusive OR the terms in the table (top down) */
crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
^ (crc << 7);
/* Combine bits shifted out left hand end */
return (crc & 0xfe) ^ ((crc >> 8) & 1);
}
/* Accumulate "dataValue" into the CRC in crcValue. */
/* Return value is updated CRC */
/* */
/* The ^ operator means exclusive OR. */
/* Note: This function is copied directly from the BACnet standard. */
uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue)
{
uint16_t crcLow;
crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
/* Exclusive OR the terms in the table (top down) */
return (crcValue >> 8) ^ (crcLow << 8) ^ (crcLow << 3)
^ (crcLow << 12) ^ (crcLow >> 4)
^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
#include "bytes.h"
/* test from Annex G 1.0 of BACnet Standard */
void testCRC8(Test * pTest)
{
uint8_t crc = 0xff; /* accumulates the crc value */
uint8_t frame_crc; /* appended to the end of the frame */
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x55);
crc = CRC_Calc_Header(0x10, crc);
ct_test(pTest, crc == 0xC2);
crc = CRC_Calc_Header(0x05, crc);
ct_test(pTest, crc == 0xBC);
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x95);
crc = CRC_Calc_Header(0x00, crc);
ct_test(pTest, crc == 0x73);
/* send the ones complement of the CRC in place of */
/* the CRC, and the resulting CRC will always equal 0x55. */
frame_crc = ~crc;
ct_test(pTest, frame_crc == 0x8C);
/* use the ones complement value and the next to last CRC value */
crc = CRC_Calc_Header(frame_crc, crc);
ct_test(pTest, crc == 0x55);
}
/* test from Annex G 2.0 of BACnet Standard */
void testCRC16(Test * pTest)
{
uint16_t crc = 0xffff;
uint16_t data_crc;
crc = CRC_Calc_Data(0x01, crc);
ct_test(pTest, crc == 0x1E0E);
crc = CRC_Calc_Data(0x22, crc);
ct_test(pTest, crc == 0xEB70);
crc = CRC_Calc_Data(0x30, crc);
ct_test(pTest, crc == 0x42EF);
/* send the ones complement of the CRC in place of */
/* the CRC, and the resulting CRC will always equal 0xF0B8. */
data_crc = ~crc;
ct_test(pTest, data_crc == 0xBD10);
crc = CRC_Calc_Data(LO_BYTE(data_crc), crc);
ct_test(pTest, crc == 0x0F3A);
crc = CRC_Calc_Data(HI_BYTE(data_crc), crc);
ct_test(pTest, crc == 0xF0B8);
}
#endif
#ifdef TEST_CRC
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("crc", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testCRC8);
assert(rc);
rc = ct_addTestFunction(pTest, testCRC16);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif
+51
View File
@@ -0,0 +1,51 @@
/*####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####*/
#ifndef CRC_H
#define CRC_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue);
uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
Binary file not shown.
+29
View File
@@ -0,0 +1,29 @@
#Makefile to build CRC tests
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_CRC -g
OBJS = crc.o test/ctest.o
TARGET = crc
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+86
View File
@@ -0,0 +1,86 @@
/*####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 DATALINK_H
#define DATALINK_H
#if defined(BACDL_ETHERNET)
#include "ethernet.h"
#define datalink_send_pdu ethernet_send_pdu
#define datalink_receive ethernet_receive
#define datalink_cleanup ethernet_cleanup
#define datalink_get_broadcast_address ethernet_get_broadcast_address
#define datalink_get_my_address ethernet_get_my_address
#elif defined(BACDL_ARCNET)
#include "arcnet.h"
#define datalink_send_pdu arcnet_send_pdu
#define datalink_receive arcnet_receive
#define datalink_cleanup arcnet_cleanup
#define datalink_get_broadcast_address arcnet_get_broadcast_address
#define datalink_get_my_address arcnet_get_my_address
#elif defined(BACDL_MSTP)
#include "dlmstp.h"
#define datalink_send_pdu dlmstp_send_pdu
#define datalink_receive dlmstp_receive
#define datalink_cleanup dlmstp_cleanup
#define datalink_get_broadcast_address dlmstp_get_broadcast_address
#define datalink_get_my_address dlmstp_get_my_address
#elif defined(BACDL_BIP)
#include "bip.h"
#define datalink_send_pdu bip_send_pdu
#define datalink_receive bip_receive
#define datalink_cleanup bip_cleanup
#define datalink_get_broadcast_address bip_get_broadcast_address
#define datalink_get_my_address bip_get_my_address
#elif defined(BACDL_TEST)
#include "npdu.h"
extern int datalink_send_pdu(BACNET_ADDRESS * dest,
BACNET_NPDU_DATA * npdu_data, uint8_t * pdu, unsigned pdu_len);
extern uint16_t datalink_receive(BACNET_ADDRESS * src,
uint8_t * pdu, uint16_t max_pdu, unsigned timeout);
extern void datalink_cleanup(void);
extern void datalink_get_broadcast_address(BACNET_ADDRESS * dest);
extern void bip_get_my_address(BACNET_ADDRESS * my_address);
#endif
#endif
+713
View File
@@ -0,0 +1,713 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
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 <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "datetime.h"
/* BACnet Date */
/* year = years since 1900 */
/* month 1=Jan */
/* day = day of month 1..31 */
/* wday 1=Monday...7=Sunday */
/* Wildcards:
A value of X'FF' in any of the four octets
shall indicate that the value is unspecified.
If all four octets = X'FF', the corresponding
time or date may be interpreted as "any" or "don't care"
*/
static bool is_leap_year(uint16_t year)
{
if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0))
return (true);
else
return (false);
}
static uint8_t month_days(uint16_t year, uint8_t month)
{
/* note: start with a zero in the first element to save us from a
month - 1 calculation in the lookup */
int month_days[13] =
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
/* February */
if ((month == 2) && is_leap_year(year))
return 29;
else if (month >= 1 && month <= 12)
return month_days[month];
else
return 0;
}
static uint32_t days_since_epoch(uint16_t year, uint8_t month, uint8_t day)
{
uint32_t days = 0; /* return value */
uint8_t monthdays; /* days in a month */
uint16_t years = 0; /* loop counter for years */
uint8_t months = 0; /* loop counter for months */
monthdays = month_days(year, month);
if ((year >= 1900) && (monthdays) && (day >= 1) && (day <= monthdays)) {
for (years = 1900; years < year; years++) {
days += 365;
if (is_leap_year(years))
days++;
}
for (months = 1; months < month; months++) {
days += month_days(years, months);
}
days += (day - 1);
}
return (days);
}
static void days_since_epoch_into_ymd(uint32_t days,
uint16_t * pYear, uint8_t * pMonth, uint8_t * pDay)
{
uint16_t year = 1900;
uint8_t month = 1;
uint8_t day = 1;
while (days >= 365) {
if ((is_leap_year(year)) && (days == 365))
break;
days -= 365;
if (is_leap_year(year))
--days;
year++;
}
while (days >= (uint32_t) month_days(year, month)) {
days -= month_days(year, month);
month++;
}
day += ((uint8_t) days);
if (pYear)
*pYear = year;
if (pMonth)
*pMonth = month;
if (pDay)
*pDay = day;
return;
}
/* Jan 1, 1900 is a Monday */
/* wday 1=Monday...7=Sunday */
static uint8_t day_of_week(uint16_t year, uint8_t month, uint8_t day)
{
return ((uint8_t) (days_since_epoch(year, month, day) % 7) + 1);
}
/* if the date1 is the same as date2, return is 0
if date1 is after date2, returns positive
if date1 is before date2, returns negative */
int datetime_compare_date(BACNET_DATE * date1, BACNET_DATE * date2)
{
int diff = 0;
if (date1 && date2) {
diff = (int) date1->year - (int) date2->year;
if (diff == 0) {
diff = (int) date1->month - (int) date2->month;
if (diff == 0) {
diff = (int) date1->day - (int) date2->day;
}
}
}
return diff;
}
/* if the time1 is the same as time2, return is 0
if time1 is after time2, returns positive
if time1 is before time2, returns negative */
int datetime_compare_time(BACNET_TIME * time1, BACNET_TIME * time2)
{
int diff = 0;
if (time1 && time2) {
diff = (int) time1->hour - (int) time2->hour;
if (diff == 0) {
diff = (int) time1->min - (int) time2->min;
if (diff == 0) {
diff = (int) time1->sec - (int) time2->sec;
if (diff == 0) {
diff =
(int) time1->hundredths - (int) time2->hundredths;
}
}
}
}
return diff;
}
/* if the datetime1 is the same as datetime2, return is 0
if datetime1 is before datetime2, returns negative
if datetime1 is after datetime2, returns positive */
int datetime_compare(BACNET_DATE_TIME * datetime1,
BACNET_DATE_TIME * datetime2)
{
int diff = 0;
diff = datetime_compare_date(&datetime1->date, &datetime2->date);
if (diff == 0) {
diff = datetime_compare_time(&datetime1->time, &datetime2->time);
}
return diff;
}
void datetime_copy_date(BACNET_DATE * dest_date, BACNET_DATE * src_date)
{
if (dest_date && src_date) {
dest_date->year = src_date->year;
dest_date->month = src_date->month;
dest_date->day = src_date->day;
dest_date->wday = src_date->wday;
}
}
void datetime_copy_time(BACNET_TIME * dest_time, BACNET_TIME * src_time)
{
if (dest_time && src_time) {
dest_time->hour = src_time->hour;
dest_time->min = src_time->min;
dest_time->sec = src_time->sec;
dest_time->hundredths = src_time->hundredths;
}
}
void datetime_copy(BACNET_DATE_TIME * dest_datetime,
BACNET_DATE_TIME * src_datetime)
{
datetime_copy_time(&dest_datetime->time, &src_datetime->time);
datetime_copy_date(&dest_datetime->date, &src_datetime->date);
}
void datetime_set_date(BACNET_DATE * bdate,
uint16_t year, uint8_t month, uint8_t day)
{
if (bdate) {
bdate->year = year;
bdate->month = month;
bdate->day = day;
bdate->wday = day_of_week(year, month, day);
}
}
void datetime_set_time(BACNET_TIME * btime,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths)
{
if (btime) {
btime->hour = hour;
btime->min = minute;
btime->sec = seconds;
btime->hundredths = hundredths;
}
}
void datetime_set(BACNET_DATE_TIME * bdatetime,
BACNET_DATE * bdate, BACNET_TIME * btime)
{
if (bdate && btime && bdatetime) {
bdatetime->time.hour = btime->hour;
bdatetime->time.min = btime->min;
bdatetime->time.sec = btime->sec;
bdatetime->time.hundredths = btime->hundredths;
bdatetime->date.year = bdate->year;
bdatetime->date.month = bdate->month;
bdatetime->date.day = bdate->day;
bdatetime->date.wday = bdate->wday;
}
}
void datetime_set_values(BACNET_DATE_TIME * bdatetime,
uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths)
{
if (bdatetime) {
bdatetime->date.year = year;
bdatetime->date.month = month;
bdatetime->date.day = day;
bdatetime->date.wday = day_of_week(year, month, day);
bdatetime->time.hour = hour;
bdatetime->time.min = minute;
bdatetime->time.sec = seconds;
bdatetime->time.hundredths = hundredths;
}
}
static uint32_t seconds_since_midnight(uint8_t hours, uint8_t minutes,
uint8_t seconds)
{
return ((hours * 60 * 60) + (minutes * 60) + seconds);
}
static void seconds_since_midnight_into_hms(uint32_t seconds,
uint8_t * pHours, uint8_t * pMinutes, uint8_t * pSeconds)
{
uint8_t hour = 0;
uint8_t minute = 0;
hour = (uint8_t) (seconds / (60 * 60));
seconds -= (hour * 60 * 60);
minute = (uint8_t) (seconds / 60);
seconds -= (minute * 60);
if (pHours)
*pHours = hour;
if (pMinutes)
*pMinutes = minute;
if (pSeconds)
*pSeconds = (uint8_t) seconds;
}
void datetime_add_minutes(BACNET_DATE_TIME * bdatetime, uint32_t minutes)
{
uint32_t bdatetime_minutes = 0;
uint32_t bdatetime_days = 0;
uint32_t days = 0;
/* convert bdatetime to seconds and days */
bdatetime_minutes = seconds_since_midnight(bdatetime->time.hour,
bdatetime->time.min, bdatetime->time.sec) / 60;
bdatetime_days = days_since_epoch(bdatetime->date.year,
bdatetime->date.month, bdatetime->date.day);
/* add */
days = minutes / (24 * 60);
bdatetime_days += days;
minutes -= (days * 24 * 60);
bdatetime_minutes += minutes;
days = bdatetime_minutes / (24 * 60);
bdatetime_days += days;
/* convert bdatetime from seconds and days */
seconds_since_midnight_into_hms(bdatetime_minutes * 60,
&bdatetime->time.hour, &bdatetime->time.min, &bdatetime->time.sec);
days_since_epoch_into_ymd(bdatetime_days,
&bdatetime->date.year,
&bdatetime->date.month, &bdatetime->date.day);
bdatetime->date.wday = day_of_week(bdatetime->date.year,
bdatetime->date.month, bdatetime->date.day);
}
bool datetime_wildcard(BACNET_DATE_TIME * bdatetime)
{
bool wildcard_present = false;
if (bdatetime) {
if ((bdatetime->date.year == (1900 + 0xFF)) &&
(bdatetime->date.month == 0xFF) &&
(bdatetime->date.day == 0xFF) &&
(bdatetime->date.wday == 0xFF) &&
(bdatetime->time.hour == 0xFF) &&
(bdatetime->time.min == 0xFF) &&
(bdatetime->time.sec == 0xFF) &&
(bdatetime->time.hundredths == 0xFF)) {
wildcard_present = true;
}
}
return wildcard_present;
}
void datetime_wildcard_set(BACNET_DATE_TIME * bdatetime)
{
if (bdatetime) {
bdatetime->date.year = 1900 + 0xFF;
bdatetime->date.month = 0xFF;
bdatetime->date.day = 0xFF;
bdatetime->date.wday = 0xFF;
bdatetime->time.hour = 0xFF;
bdatetime->time.min = 0xFF;
bdatetime->time.sec = 0xFF;
bdatetime->time.hundredths = 0xFF;
}
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testBACnetDateTimeWildcard(Test * pTest)
{
BACNET_DATE_TIME bdatetime;
bool status = false;
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
status = datetime_wildcard(&bdatetime);
ct_test(pTest, status == false);
datetime_wildcard_set(&bdatetime);
status = datetime_wildcard(&bdatetime);
ct_test(pTest, status == true);
}
void testBACnetDateTimeAdd(Test * pTest)
{
BACNET_DATE_TIME bdatetime, test_bdatetime;
uint32_t minutes = 0;
int diff = 0;
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_copy(&test_bdatetime, &bdatetime);
datetime_add_minutes(&bdatetime, minutes);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, 60);
datetime_set_values(&test_bdatetime, 1900, 1, 1, 1, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, (24 * 60));
datetime_set_values(&test_bdatetime, 1900, 1, 2, 0, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
datetime_set_values(&bdatetime, 1900, 1, 1, 0, 0, 0, 0);
datetime_add_minutes(&bdatetime, (31 * 24 * 60));
datetime_set_values(&test_bdatetime, 1900, 2, 1, 0, 0, 0, 0);
diff = datetime_compare(&test_bdatetime, &bdatetime);
ct_test(pTest, diff == 0);
}
void testBACnetDateTimeSeconds(Test * pTest)
{
uint8_t hour = 0, minute = 0, second = 0;
uint8_t test_hour = 0, test_minute = 0, test_second = 0;
uint32_t seconds = 0, test_seconds;
for (hour = 0; hour < 24; hour++) {
for (minute = 0; minute < 60; minute += 3) {
for (second = 0; second < 60; second += 17) {
seconds = seconds_since_midnight(hour, minute, second);
seconds_since_midnight_into_hms(seconds,
&test_hour, &test_minute, &test_second);
test_seconds =
seconds_since_midnight(test_hour, test_minute,
test_second);
ct_test(pTest, seconds == test_seconds);
}
}
}
}
void testBACnetDate(Test * pTest)
{
BACNET_DATE bdate1, bdate2;
int diff = 0;
datetime_set_date(&bdate1, 1900, 1, 1);
datetime_copy_date(&bdate2, &bdate1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff == 0);
datetime_set_date(&bdate2, 1900, 1, 2);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 1900, 2, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 1901, 1, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
/* midpoint */
datetime_set_date(&bdate1, 2007, 7, 15);
datetime_copy_date(&bdate2, &bdate1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff == 0);
datetime_set_date(&bdate2, 2007, 7, 14);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 7, 1);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 7, 31);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 8, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 12, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2007, 6, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2007, 1, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2006, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 1900, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff > 0);
datetime_set_date(&bdate2, 2008, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
datetime_set_date(&bdate2, 2154, 7, 15);
diff = datetime_compare_date(&bdate1, &bdate2);
ct_test(pTest, diff < 0);
return;
}
void testBACnetTime(Test * pTest)
{
BACNET_TIME btime1, btime2;
int diff = 0;
datetime_set_time(&btime1, 0, 0, 0, 0);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime1, 23, 59, 59, 99);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
/* midpoint */
datetime_set_time(&btime1, 12, 30, 30, 50);
datetime_copy_time(&btime2, &btime1);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime2, 12, 30, 30, 51);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 30, 31, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 31, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 13, 30, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff < 0);
datetime_set_time(&btime2, 12, 30, 30, 49);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 12, 30, 29, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 12, 29, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
datetime_set_time(&btime2, 11, 30, 30, 50);
diff = datetime_compare_time(&btime1, &btime2);
ct_test(pTest, diff > 0);
return;
}
void testBACnetDateTime(Test * pTest)
{
BACNET_DATE_TIME bdatetime1, bdatetime2;
BACNET_DATE bdate;
BACNET_TIME btime;
int diff = 0;
datetime_set_values(&bdatetime1, 1900, 1, 1, 0, 0, 0, 0);
datetime_copy(&bdatetime2, &bdatetime1);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff == 0);
datetime_set_time(&btime, 0, 0, 0, 0);
datetime_set_date(&bdate, 1900, 1, 1);
datetime_set(&bdatetime1, &bdate, &btime);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff == 0);
/* midpoint */
/* if datetime1 is before datetime2, returns negative */
datetime_set_values(&bdatetime1, 2000, 7, 15, 12, 30, 30, 50);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 51);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 31, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 31, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 13, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 16, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 8, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2001, 7, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff < 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 30, 49);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 30, 29, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 12, 29, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 15, 11, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 7, 14, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 2000, 6, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
datetime_set_values(&bdatetime2, 1999, 7, 15, 12, 30, 30, 50);
diff = datetime_compare(&bdatetime1, &bdatetime2);
ct_test(pTest, diff > 0);
return;
}
void testDateEpoch(Test * pTest)
{
uint32_t days = 0;
uint16_t year = 0, test_year = 0;
uint8_t month = 0, test_month = 0;
uint8_t day = 0, test_day = 0;
days = days_since_epoch(1900, 1, 1);
ct_test(pTest, days == 0);
days_since_epoch_into_ymd(days, &year, &month, &day);
ct_test(pTest, year == 1900);
ct_test(pTest, month == 1);
ct_test(pTest, day == 1);
for (year = 1900; year <= 2154; year++) {
for (month = 1; month <= 12; month++) {
for (day = 1; day <= month_days(year, month); day++) {
days = days_since_epoch(year, month, day);
days_since_epoch_into_ymd(days,
&test_year, &test_month, &test_day);
ct_test(pTest, year == test_year);
ct_test(pTest, month == test_month);
ct_test(pTest, day == test_day);
}
}
}
}
void testBACnetDayOfWeek(Test * pTest)
{
uint8_t dow = 0;
/* 1/1/1900 is a Monday */
dow = day_of_week(1900, 1, 1);
ct_test(pTest, dow == 1);
/* 1/1/2007 is a Monday */
dow = day_of_week(2007, 1, 1);
ct_test(pTest, dow == 1);
dow = day_of_week(2007, 1, 2);
ct_test(pTest, dow == 2);
dow = day_of_week(2007, 1, 3);
ct_test(pTest, dow == 3);
dow = day_of_week(2007, 1, 4);
ct_test(pTest, dow == 4);
dow = day_of_week(2007, 1, 5);
ct_test(pTest, dow == 5);
dow = day_of_week(2007, 1, 6);
ct_test(pTest, dow == 6);
dow = day_of_week(2007, 1, 7);
ct_test(pTest, dow == 7);
dow = day_of_week(2007, 1, 31);
ct_test(pTest, dow == 3);
}
#ifdef TEST_DATE_TIME
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Date Time", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testBACnetDate);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetTime);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTime);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDayOfWeek);
assert(rc);
rc = ct_addTestFunction(pTest, testDateEpoch);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeSeconds);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeAdd);
assert(rc);
rc = ct_addTestFunction(pTest, testBACnetDateTimeWildcard);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DATE_TIME */
#endif /* TEST */
+112
View File
@@ -0,0 +1,112 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2007 Steve Karg <skarg@users.sourceforge.net>
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 DATE_TIME_H
#define DATE_TIME_H
#include <stdint.h>
#include <stdbool.h>
typedef enum {
BACNET_WEEKDAY_MONDAY = 1,
BACNET_WEEKDAY_TUESDAY = 2,
BACNET_WEEKDAY_WEDNESDAY = 3,
BACNET_WEEKDAY_THURSDAY = 4,
BACNET_WEEKDAY_FRIDAY = 5,
BACNET_WEEKDAY_SATURDAY = 6,
BACNET_WEEKDAY_SUNDAY = 7
} BACNET_WEEKDAY;
/* date */
typedef struct BACnet_Date {
uint16_t year; /* AD */
uint8_t month; /* 1=Jan */
uint8_t day; /* 1..31 */
uint8_t wday; /* 1=Monday-7=Sunday */
} BACNET_DATE;
/* time */
typedef struct BACnet_Time {
uint8_t hour;
uint8_t min;
uint8_t sec;
uint8_t hundredths;
} BACNET_TIME;
typedef struct BACnet_DateTime {
BACNET_DATE date;
BACNET_TIME time;
} BACNET_DATE_TIME;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* utility initialization functions */
void datetime_set_date(BACNET_DATE * bdate,
uint16_t year, uint8_t month, uint8_t day);
void datetime_set_time(BACNET_TIME * btime,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths);
void datetime_set(BACNET_DATE_TIME * bdatetime,
BACNET_DATE * bdate, BACNET_TIME * btime);
void datetime_set_values(BACNET_DATE_TIME * bdatetime,
uint16_t year, uint8_t month, uint8_t day,
uint8_t hour, uint8_t minute, uint8_t seconds, uint8_t hundredths);
/* utility comparison functions:
if the date/times are the same, return is 0
if date1 is before date2, returns negative
if date1 is after date2, returns positive */
int datetime_compare_date(BACNET_DATE * date1, BACNET_DATE * date2);
int datetime_compare_time(BACNET_TIME * time1, BACNET_TIME * time2);
int datetime_compare(BACNET_DATE_TIME * datetime1,
BACNET_DATE_TIME * datetime2);
/* utility copy functions */
void datetime_copy_date(BACNET_DATE * date1, BACNET_DATE * date2);
void datetime_copy_time(BACNET_TIME * time1, BACNET_TIME * time2);
void datetime_copy(BACNET_DATE_TIME * datetime1,
BACNET_DATE_TIME * datetime2);
/* utility add function */
void datetime_add_minutes(BACNET_DATE_TIME * bdatetime,
uint32_t minutes);
/* date and time wildcards */
bool datetime_wildcard(BACNET_DATE_TIME * bdatetime);
void datetime_wildcard_set(BACNET_DATE_TIME * bdatetime);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* DATE_TIME_H */
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_DATE_TIME -g
SRCS = datetime.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = datetime
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+308
View File
@@ -0,0 +1,308 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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 "bacenum.h"
#include "bacdcode.h"
#include "bacdef.h"
#include "dcc.h"
/* note: the disable and time are not expected to survive
over a power cycle or reinitialization. */
/* note: time duration is given in Minutes, but in order to be accurate,
we need to count down in seconds. */
static uint32_t DCC_Time_Duration_Seconds = 0;
static BACNET_COMMUNICATION_ENABLE_DISABLE DCC_Enable_Disable =
COMMUNICATION_ENABLE;
/* password is optionally supported */
BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status(void)
{
return DCC_Enable_Disable;
}
bool dcc_communication_enabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_ENABLE);
}
/* When network communications are completely disabled,
only DeviceCommunicationControl and ReinitializeDevice APDUs
shall be processed and no messages shall be initiated.*/
bool dcc_communication_disabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE);
}
/* When the initiation of communications is disabled,
all APDUs shall be processed and responses returned as
required and no messages shall be initiated with the
exception of I-Am requests, which shall be initiated only in
response to Who-Is messages. In this state, a device that
supports I-Am request initiation shall send one I-Am request
for any Who-Is request that is received if and only if
the Who-Is request does not contain an address range or
the device is included in the address range. */
bool dcc_communication_initiation_disabled(void)
{
return (DCC_Enable_Disable == COMMUNICATION_DISABLE_INITIATION);
}
uint32_t dcc_duration_seconds(void)
{
return DCC_Time_Duration_Seconds;
}
/* called every second or so. If more than one second,
then seconds should be the number of seconds to tick away */
void dcc_timer_seconds(uint32_t seconds)
{
if (DCC_Time_Duration_Seconds) {
if (DCC_Time_Duration_Seconds > seconds)
DCC_Time_Duration_Seconds -= seconds;
else
DCC_Time_Duration_Seconds = 0;
/* just expired - do something */
if (DCC_Time_Duration_Seconds == 0)
DCC_Enable_Disable = COMMUNICATION_ENABLE;
}
}
bool dcc_set_status_duration(BACNET_COMMUNICATION_ENABLE_DISABLE status,
uint16_t minutes)
{
bool valid = false;
/* valid? */
if (status < MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) {
DCC_Enable_Disable = status;
if (status == COMMUNICATION_ENABLE)
DCC_Time_Duration_Seconds = 0;
else
DCC_Time_Duration_Seconds = minutes * 60;
valid = true;
}
return valid;
}
/* encode service */
int dcc_encode_apdu(uint8_t * apdu, uint8_t invoke_id, uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING * password)
{ /* NULL=optional */
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_DEVICE_COMMUNICATION_CONTROL;
apdu_len = 4;
/* optional timeDuration */
if (timeDuration) {
len =
encode_context_unsigned(&apdu[apdu_len], 0, timeDuration);
apdu_len += len;
}
/* enable disable */
len =
encode_context_enumerated(&apdu[apdu_len], 1, enable_disable);
apdu_len += len;
/* optional password */
if (password) {
/* FIXME: must be at least 1 character, limited to 20 characters */
len =
encode_context_character_string(&apdu[apdu_len], 2,
password);
apdu_len += len;
}
}
return apdu_len;
}
/* decode the service request only */
int dcc_decode_service_request(uint8_t * apdu,
unsigned apdu_len,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password)
{
unsigned len = 0;
uint8_t tag_number = 0;
uint32_t len_value_type = 0;
int value = 0;
uint32_t value32 = 0;
/* check for value pointers */
if (apdu_len) {
/* Tag 0: timeDuration --optional-- */
if (decode_is_context_tag(&apdu[len], 0)) {
len += decode_tag_number_and_value(&apdu[len], &tag_number,
&len_value_type);
len += decode_unsigned(&apdu[len], len_value_type, &value32);
if (timeDuration)
*timeDuration = (uint16_t) value32;
} else if (timeDuration)
*timeDuration = 0;
/* Tag 1: enable_disable */
if (!decode_is_context_tag(&apdu[len], 1))
return -1;
len += decode_tag_number_and_value(&apdu[len],
&tag_number, &len_value_type);
len += decode_enumerated(&apdu[len], len_value_type, &value);
if (enable_disable)
*enable_disable = value;
/* Tag 2: password --optional-- */
if (len < apdu_len) {
if (!decode_is_context_tag(&apdu[len], 2))
return -1;
len += decode_tag_number_and_value(&apdu[len],
&tag_number, &len_value_type);
len +=
decode_character_string(&apdu[len], len_value_type,
password);
} else if (password)
characterstring_init_ansi(password, NULL);
}
return (int) len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
int dcc_decode_apdu(uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password)
{
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, Device_Max_APDU_Length_Accepted()); */
*invoke_id = apdu[2]; /* invoke id - filled in by net layer */
if (apdu[3] != SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL)
return -1;
offset = 4;
if (apdu_len > offset) {
len = dcc_decode_service_request(&apdu[offset],
apdu_len - offset, timeDuration, enable_disable, password);
}
return len;
}
void test_DeviceCommunicationControlData(Test * pTest,
uint8_t invoke_id,
uint16_t timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable,
BACNET_CHARACTER_STRING * password)
{
uint8_t apdu[480] = { 0 };
int len = 0;
int apdu_len = 0;
uint8_t test_invoke_id = 0;
uint16_t test_timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE test_enable_disable;
BACNET_CHARACTER_STRING test_password;
len = dcc_encode_apdu(&apdu[0],
invoke_id, timeDuration, enable_disable, password);
ct_test(pTest, len != 0);
apdu_len = len;
len = dcc_decode_apdu(&apdu[0],
apdu_len,
&test_invoke_id,
&test_timeDuration, &test_enable_disable, &test_password);
ct_test(pTest, len != -1);
ct_test(pTest, test_invoke_id == invoke_id);
ct_test(pTest, test_timeDuration == timeDuration);
ct_test(pTest, test_enable_disable == enable_disable);
ct_test(pTest, characterstring_same(&test_password, password));
}
void test_DeviceCommunicationControl(Test * pTest)
{
uint8_t invoke_id = 128;
uint16_t timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable;
BACNET_CHARACTER_STRING password;
timeDuration = 0;
enable_disable = COMMUNICATION_DISABLE_INITIATION;
characterstring_init_ansi(&password, "John 3:16");
test_DeviceCommunicationControlData(pTest,
invoke_id, timeDuration, enable_disable, &password);
timeDuration = 12345;
enable_disable = COMMUNICATION_DISABLE;
test_DeviceCommunicationControlData(pTest,
invoke_id, timeDuration, enable_disable, NULL);
return;
}
#ifdef TEST_DEVICE_COMMUNICATION_CONTROL
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet DeviceCommunicationControl", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, test_DeviceCommunicationControl);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_DEVICE_COMMUNICATION_CONTROL */
#endif /* TEST */
+86
View File
@@ -0,0 +1,86 @@
/*####COPYRIGHTBEGIN####
-------------------------------------------
Copyright (C) 2006 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 DCC_H
#define DCC_H
#include <stdint.h>
#include <stdbool.h>
#include "bacenum.h"
#include "bacstr.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* return the status */
BACNET_COMMUNICATION_ENABLE_DISABLE dcc_enable_status(void);
bool dcc_communication_enabled(void);
bool dcc_communication_disabled(void);
bool dcc_communication_initiation_disabled(void);
/* return the time */
uint32_t dcc_duration_seconds(void);
/* called every second or so. If more than one second,
then seconds should be the number of seconds to tick away */
void dcc_timer_seconds(uint32_t seconds);
/* setup the communication values */
bool dcc_set_status_duration(BACNET_COMMUNICATION_ENABLE_DISABLE
status, uint16_t minutes);
/* encode service */
int dcc_encode_apdu(uint8_t * apdu, uint8_t invoke_id, uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE enable_disable, BACNET_CHARACTER_STRING * password); /* NULL=optional */
/* decode the service request only */
int dcc_decode_service_request(uint8_t * apdu,
unsigned apdu_len,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password);
#ifdef TEST
#include "ctest.h"
int dcc_decode_apdu(uint8_t * apdu,
unsigned apdu_len,
uint8_t * invoke_id,
uint16_t * timeDuration,
BACNET_COMMUNICATION_ENABLE_DISABLE * enable_disable,
BACNET_CHARACTER_STRING * password);
void test_DeviceCommunicationControl(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+34
View File
@@ -0,0 +1,34 @@
#Makefile to build test case
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -g
CFLAGS = -Wall -I. -Itest -DTEST -DTEST_DEVICE_COMMUNICATION_CONTROL -g
SRCS = bacdcode.c \
bacstr.c \
bigend.c \
dcc.c \
test/ctest.c
OBJS = ${SRCS:.c=.o}
TARGET = dcc
all: ${TARGET}
${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) *.bak *.1 *.ini
include: .depend
+86
View File
@@ -0,0 +1,86 @@
#Makefile to build BACnet Application for the Linux Port
CC = gcc
BASEDIR = .
#CFLAGS = -Wall -I.
# -g for debugging with gdb
#CFLAGS = -Wall -I. -O2 -g
# Note: you can strip out symbols using the strip command
# to get an idea of how big the compile really is.
#DEFINES = -DBACFILE=1 -DBACDL_ETHERNET=1
#DEFINES = -DBACFILE=1 -DBACDL_ARCNET=1
#DEFINES = -DBACFILE=1 -DBACDL_MSTP=1
DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBACDL_BIP=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1
BACNET_PORT = ../../ports/linux
BACNET_OBJECT = ../object
BACNET_HANDLER = ../handler
BACNET_ROOT = ../..
INCLUDES = -I$(BACNET_ROOT) -I$(BACNET_PORT) -I$(BACNET_OBJECT) -I$(BACNET_HANDLER)
CFLAGS = -Wall -g $(INCLUDES) $(DEFINES)
TARGET = bacdcc
SRCS = main.c \
$(BACNET_ROOT)/dcc.c \
$(BACNET_PORT)/bip-init.c \
$(BACNET_ROOT)/bip.c \
$(BACNET_HANDLER)/txbuf.c \
$(BACNET_HANDLER)/noserv.c \
$(BACNET_HANDLER)/h_whois.c \
$(BACNET_HANDLER)/h_rp.c \
$(BACNET_HANDLER)/h_iam.c \
$(BACNET_HANDLER)/h_dcc.c \
$(BACNET_HANDLER)/s_whois.c \
$(BACNET_HANDLER)/s_dcc.c \
$(BACNET_OBJECT)/device.c \
$(BACNET_OBJECT)/ai.c \
$(BACNET_OBJECT)/ao.c \
$(BACNET_OBJECT)/av.c \
$(BACNET_OBJECT)/bi.c \
$(BACNET_OBJECT)/bo.c \
$(BACNET_OBJECT)/bv.c \
$(BACNET_OBJECT)/lc.c \
$(BACNET_OBJECT)/lsp.c \
$(BACNET_OBJECT)/mso.c \
$(BACNET_OBJECT)/bacfile.c \
$(BACNET_ROOT)/filename.c \
$(BACNET_ROOT)/rp.c \
$(BACNET_ROOT)/wp.c \
$(BACNET_ROOT)/bacdcode.c \
$(BACNET_ROOT)/bacapp.c \
$(BACNET_ROOT)/bacprop.c \
$(BACNET_ROOT)/bacstr.c \
$(BACNET_ROOT)/bactext.c \
$(BACNET_ROOT)/datetime.c \
$(BACNET_ROOT)/indtext.c \
$(BACNET_ROOT)/bigend.c \
$(BACNET_ROOT)/whois.c \
$(BACNET_ROOT)/iam.c \
$(BACNET_ROOT)/tsm.c \
$(BACNET_ROOT)/address.c \
$(BACNET_ROOT)/arf.c \
$(BACNET_ROOT)/abort.c \
$(BACNET_ROOT)/reject.c \
$(BACNET_ROOT)/bacerror.c \
$(BACNET_ROOT)/apdu.c \
$(BACNET_ROOT)/npdu.c
OBJS = ${SRCS:.c=.o}
all: ${TARGET}
${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) *.bak ports/linux/*.bak *.1 *.ini
include: .depend
+282
View File
@@ -0,0 +1,282 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
/* command line tool demo for BACnet stack */
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h> /* for time */
#include <errno.h>
#include "bactext.h"
#include "iam.h"
#include "arf.h"
#include "tsm.h"
#include "address.h"
#include "config.h"
#include "bacdef.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "net.h"
#include "datalink.h"
#include "whois.h"
#include "dcc.h"
/* some demo stuff needed */
#include "filename.h"
#include "handlers.h"
#include "client.h"
#include "txbuf.h"
/* buffer used for receive */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* global variables used in this file */
static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE;
static BACNET_ADDRESS Target_Address;
static uint16_t Communication_Timeout_Minutes = 0;
static BACNET_COMMUNICATION_ENABLE_DISABLE Communication_State =
COMMUNICATION_ENABLE;
static char *Communication_Password = NULL;
static bool Error_Detected = false;
static void MyErrorHandler(BACNET_ADDRESS * src,
uint8_t invoke_id,
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("BACnet Error: %s: %s\r\n",
bactext_error_class_name(error_class),
bactext_error_code_name(error_code));
Error_Detected = true;
}
void MyAbortHandler(BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t abort_reason, bool server)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
(void) server;
printf("BACnet Abort: %s\r\n",
bactext_abort_reason_name(abort_reason));
Error_Detected = true;
}
void MyRejectHandler(BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t reject_reason)
{
/* FIXME: verify src and invoke id */
(void) src;
(void) invoke_id;
printf("BACnet Reject: %s\r\n",
bactext_reject_reason_name(reject_reason));
Error_Detected = true;
}
void MyDeviceCommunicationControlSimpleAckHandler(BACNET_ADDRESS * src,
uint8_t invoke_id)
{
(void) src;
(void) invoke_id;
printf("DeviceCommunicationControl Acknowledged!\r\n");
}
static void Init_Service_Handlers(void)
{
/* we need to handle who-is
to support dynamic device binding to us */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
handler_who_is);
/* handle i-am to support binding to other devices */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
handler_i_am_bind);
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler
(handler_unrecognized_service);
/* we must implement read property - it's required! */
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
handler_read_property);
/* handle communication so we can shutup when asked */
apdu_set_confirmed_handler
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
handler_device_communication_control);
/* handle the ack coming back */
apdu_set_confirmed_simple_ack_handler
(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
MyDeviceCommunicationControlSimpleAckHandler);
/* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
MyErrorHandler);
apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler);
}
#ifdef BIP_DEBUG
static void print_address(char *name, BACNET_ADDRESS * dest)
{ /* destination address */
int i = 0; /* counter */
if (dest) {
printf("%s: ", name);
for (i = 0; i < dest->mac_len; i++) {
printf("%02X", dest->mac[i]);
}
printf("\n");
}
}
#endif
int main(int argc, char *argv[])
{
BACNET_ADDRESS src = { 0 }; /* address where message came from */
uint16_t pdu_len = 0;
unsigned timeout = 100; /* milliseconds */
unsigned max_apdu = 0;
time_t elapsed_seconds = 0;
time_t last_seconds = 0;
time_t current_seconds = 0;
time_t timeout_seconds = 0;
uint8_t invoke_id = 0;
bool found = false;
#ifdef BIP_DEBUG
BACNET_ADDRESS my_address, broadcast_address;
#endif
if (argc < 3) {
/* note: priority 16 and 0 should produce the same end results... */
printf("Usage: %s device-instance state timeout [password]\r\n"
"Send BACnet DeviceCommunicationControl service to device.\r\n"
"\r\n"
"The device-instance can be 0 to %d.\r\n"
"Possible state values:\r\n"
" 0=enable\r\n"
" 1=disable\r\n"
" 2=disable-initiation\r\n"
"The timeout can be 0 for infinite, or a value in minutes for disable.\r\n"
"The optional password is a character string of 1 to 20 characters.\r\n",
filename_remove_path(argv[0]), BACNET_MAX_INSTANCE - 1);
return 0;
}
/* decode the command line parameters */
Target_Device_Object_Instance = strtol(argv[1], NULL, 0);
Communication_State = strtol(argv[2], NULL, 0);
Communication_Timeout_Minutes = strtol(argv[3], NULL, 0);
/* optional password */
if (argc > 4)
Communication_Password = argv[4];
if (Target_Device_Object_Instance >= BACNET_MAX_INSTANCE) {
fprintf(stderr, "device-instance=%u - it must be less than %u\r\n",
Target_Device_Object_Instance, BACNET_MAX_INSTANCE);
return 1;
}
/* setup my info */
Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE);
address_init();
Init_Service_Handlers();
#ifdef BACDL_ETHERNET
/* init the physical layer */
if (!ethernet_init("eth0"))
return 1;
#endif
#ifdef BACDL_BIP
bip_set_interface("eth0");
if (!bip_init())
return 1;
printf("bip: using port %hu\r\n", bip_get_port());
#ifdef BIP_DEBUG
datalink_get_broadcast_address(&broadcast_address);
print_address("Broadcast", &broadcast_address);
datalink_get_my_address(&my_address);
print_address("Address", &my_address);
#endif
#endif
#ifdef BACDL_ARCNET
if (!arcnet_init("arc0"))
return 1;
#endif
/* configure the timeout values */
last_seconds = time(NULL);
timeout_seconds = (Device_APDU_Timeout() / 1000) *
Device_Number_Of_APDU_Retries();
/* try to bind with the device */
Send_WhoIs(Target_Device_Object_Instance,
Target_Device_Object_Instance);
/* loop forever */
for (;;) {
/* increment timer - exit if timed out */
current_seconds = time(NULL);
/* returns 0 bytes on timeout */
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
/* process */
if (pdu_len) {
npdu_handler(&src, &Rx_Buf[0], pdu_len);
}
/* at least one second has passed */
if (current_seconds != last_seconds)
tsm_timer_milliseconds(((current_seconds -
last_seconds) * 1000));
if (Error_Detected)
break;
/* wait until the device is bound, or timeout and quit */
found = address_bind_request(Target_Device_Object_Instance,
&max_apdu, &Target_Address);
if (found) {
if (invoke_id == 0) {
invoke_id =
Send_Device_Communication_Control_Request
(Target_Device_Object_Instance,
Communication_Timeout_Minutes, Communication_State,
Communication_Password);
} else if (tsm_invoke_id_free(invoke_id))
break;
else if (tsm_invoke_id_failed(invoke_id)) {
fprintf(stderr, "\rError: TSM Timeout!\r\n");
tsm_free_invoke_id(invoke_id);
/* try again or abort? */
break;
}
} else {
/* increment timer - exit if timed out */
elapsed_seconds += (current_seconds - last_seconds);
if (elapsed_seconds > timeout_seconds) {
printf("\rError: APDU Timeout!\r\n");
break;
}
}
/* keep track of time for next check */
last_seconds = current_seconds;
}
return 0;
}
+157
View File
@@ -0,0 +1,157 @@
#
# Simple makefile to build an executable for Win32
#
# This makefile assumes Borland bcc32 development environment
# on Windows NT/9x/2000/XP
#
!ifndef BORLAND_DIR
BORLAND_DIR_Not_Defined:
@echo .
@echo You must define environment variable BORLAND_DIR to compile.
!endif
PRODUCT = bacdcc
PRODUCT_EXE = $(PRODUCT).exe
# Choose the Data Link Layer to Enable
DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;USE_INADDR=1;BIP_DEBUG;BIG_ENDIAN=0;PRINT_ENABLED=1
SRCS = main.c \
..\..\ports\win32\bip-init.c \
..\..\filename.c \
..\..\bip.c \
..\..\demo\handler\txbuf.c \
..\..\demo\handler\noserv.c \
..\..\demo\handler\h_whois.c \
..\..\demo\handler\h_iam.c \
..\..\demo\handler\h_rp.c \
..\..\demo\handler\h_dcc.c \
..\..\demo\handler\s_whois.c \
..\..\demo\handler\s_dcc.c \
..\..\bacdcode.c \
..\..\bacapp.c \
..\..\bacstr.c \
..\..\bactext.c \
..\..\datetime.c \
..\..\indtext.c \
..\..\bigend.c \
..\..\whois.c \
..\..\iam.c \
..\..\rp.c \
..\..\wp.c \
..\..\arf.c \
..\..\awf.c \
..\..\dcc.c \
..\..\demo\object\bacfile.c \
..\..\demo\object\device.c \
..\..\demo\object\ai.c \
..\..\demo\object\ao.c \
..\..\demo\object\av.c \
..\..\demo\object\bi.c \
..\..\demo\object\bo.c \
..\..\demo\object\bv.c \
..\..\demo\object\lc.c \
..\..\demo\object\lsp.c \
..\..\demo\object\mso.c \
..\..\tsm.c \
..\..\address.c \
..\..\abort.c \
..\..\reject.c \
..\..\bacerror.c \
..\..\apdu.c \
..\..\npdu.c
OBJS = $(SRCS:.c=.obj)
# Compiler definitions
#
CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg
#LINK = $(BORLAND_DIR)\bin\tlink32
LINK = $(BORLAND_DIR)\bin\ilink32
TLIB = $(BORLAND_DIR)\bin\tlib
#
# Include directories
#
CC_DIR = $(BORLAND_DIR)\BIN
INCL_DIRS = -I$(BORLAND_DIR)\include;..\..\;..\..\demo\object\;..\..\demo\handler\;..\..\ports\win32\;.
CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES)
# Libraries
#
C_LIB_DIR = $(BORLAND_DIR)\lib
LIBS = $(C_LIB_DIR)\IMPORT32.lib \
$(C_LIB_DIR)\CW32MT.lib
#
# Main target
#
# This should be the first one in the makefile
all : bcc32.cfg $(PRODUCT_EXE)
install: $(PRODUCT_EXE)
copy $(PRODUCT_EXE) ..\..\utils\$(PRODUCT_EXE)
# Linker specific: the link below is for BCC linker/compiler. If you link
# with a different linker - please change accordingly.
#
# need a temp response file (@&&) because command line is too long
$(PRODUCT_EXE) : $(OBJS)
@echo Running Linker for $(PRODUCT_EXE)
$(LINK) -L$(C_LIB_DIR) -m -c -s -v @&&| # temp response file, starts with |
$(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency
$<
$*.map
$(LIBS)
| # end of temp response file
#
# Utilities
clean :
@echo Deleting obj files, $(PRODUCT_EXE) and map files.
# del $(OBJS) # command too long, bummer!
del *.obj
del ..\..\*.obj
del ..\..\demo\handler\*.obj
del ..\..\demo\object\*.obj
del ..\..\ports\win32\*.obj
del $(PRODUCT_EXE)
del *.map
del bcc32.cfg
#
# Generic rules
#
.SUFFIXES: .cpp .c .sbr .obj
#
# cc generic rule
#
.c.obj:
$(CC) -o$@ $<
# Compiler configuration file
bcc32.cfg :
Copy &&|
$(CFLAGS)
-c
-y #include line numbers in OBJ's
-v #include debug info
-w+ #turn on all warnings
-Od #disable all optimizations
#-a4 #32 bit data alignment
#-M # generate link map
#-ls # linker options
#-WM- #not multithread
-WM #multithread
-w-aus # ignore warning assigned a value that is never used
-w-sig # ignore warning conversion may lose sig digits
| $@
# EOF: makefile
+506
View File
@@ -0,0 +1,506 @@
#############################################################################
# Makefile for building bacdcc
# Generated by tmake at 11:27, 2006/05/10
# Project: tmake
# Template: app
#############################################################################
####### Compiler, tools and options
QTDIR = /usr
CC = gcc
CXX = g++
CFLAGS = -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG
CXXFLAGS= -pipe -Wall -W -g -DBACDL_BIP=1 -DTSM_ENABLED=1 -DUSE_INADDR=1 -DBIP_DEBUG
INCPATH = -I. -I../.. -I../../demo/object -I../../demo/handler -I../../ports/linux
LINK = g++
LFLAGS =
LIBS = $(SUBLIBS)
MOC = $(QTDIR)/bin/moc
UIC = $(QTDIR)/bin/uic
TAR = tar -cf
GZIP = gzip -9f
####### Files
HEADERS =
SOURCES = main.c \
../../filename.c \
../../bip.c \
../../demo/handler/txbuf.c \
../../demo/handler/noserv.c \
../../demo/handler/h_whois.c \
../../demo/handler/h_iam.c \
../../demo/handler/h_rp.c \
../../demo/handler/h_dcc.c \
../../demo/handler/s_whois.c \
../../demo/handler/s_dcc.c \
../../bacdcode.c \
../../bacapp.c \
../../bacstr.c \
../../bactext.c \
../../indtext.c \
../../bigend.c \
../../whois.c \
../../iam.c \
../../rp.c \
../../wp.c \
../../arf.c \
../../awf.c \
../../dcc.c \
../../demo/object/bacfile.c \
../../demo/object/device.c \
../../demo/object/ai.c \
../../demo/object/ao.c \
../../demo/object/bi.c \
../../demo/object/bo.c \
../../demo/object/lsp.c \
../../datalink.c \
../../tsm.c \
../../address.c \
../../abort.c \
../../reject.c \
../../bacerror.c \
../../apdu.c \
../../npdu.c \
../../ports/linux/bip-init.c
OBJECTS = main.o \
../../filename.o \
../../bip.o \
../../demo/handler/txbuf.o \
../../demo/handler/noserv.o \
../../demo/handler/h_whois.o \
../../demo/handler/h_iam.o \
../../demo/handler/h_rp.o \
../../demo/handler/h_dcc.o \
../../demo/handler/s_whois.o \
../../demo/handler/s_dcc.o \
../../bacdcode.o \
../../bacapp.o \
../../bacstr.o \
../../bactext.o \
../../indtext.o \
../../bigend.o \
../../whois.o \
../../iam.o \
../../rp.o \
../../wp.o \
../../arf.o \
../../awf.o \
../../dcc.o \
../../demo/object/bacfile.o \
../../demo/object/device.o \
../../demo/object/ai.o \
../../demo/object/ao.o \
../../demo/object/bi.o \
../../demo/object/bo.o \
../../demo/object/lsp.o \
../../datalink.o \
../../tsm.o \
../../address.o \
../../abort.o \
../../reject.o \
../../bacerror.o \
../../apdu.o \
../../npdu.o \
../../ports/linux/bip-init.o
INTERFACES =
UICDECLS =
UICIMPLS =
SRCMOC =
OBJMOC =
DIST =
TARGET = bacdcc
INTERFACE_DECL_PATH = .
####### Implicit rules
.SUFFIXES: .cpp .cxx .cc .C .c
.cpp.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.cxx.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.cc.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.C.o:
$(CXX) -c $(CXXFLAGS) $(INCPATH) -o $@ $<
.c.o:
$(CC) -c $(CFLAGS) $(INCPATH) -o $@ $<
####### Build rules
all: $(TARGET)
$(TARGET): $(UICDECLS) $(OBJECTS) $(OBJMOC)
$(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(OBJMOC) $(LIBS)
moc: $(SRCMOC)
tmake: makefile.g++
makefile.g++: tmake.pro
tmake tmake.pro -o makefile.g++
dist:
$(TAR) tmake.tar tmake.pro $(SOURCES) $(HEADERS) $(INTERFACES) $(DIST)
$(GZIP) tmake.tar
clean:
-rm -f $(OBJECTS) $(OBJMOC) $(SRCMOC) $(UICIMPLS) $(UICDECLS) $(TARGET)
-rm -f *~ core
-rm -f core *~
####### Sub-libraries
###### Combined headers
####### Compile
main.o: main.c
../../filename.o: ../../filename.c
../../bip.o: ../../bip.c \
../../bacdcode.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacstr.h \
../../bip.h
../../demo/handler/txbuf.o: ../../demo/handler/txbuf.c \
../../config.h
../../demo/handler/noserv.o: ../../demo/handler/noserv.c \
../../demo/handler/txbuf.h \
../../config.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h
../../demo/handler/h_whois.o: ../../demo/handler/h_whois.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h \
../../demo/handler/client.h
../../demo/handler/h_iam.o: ../../demo/handler/h_iam.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h
../../demo/handler/h_rp.o: ../../demo/handler/h_rp.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h
../../demo/handler/h_dcc.o: ../../demo/handler/h_dcc.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h
../../demo/handler/s_whois.o: ../../demo/handler/s_whois.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h \
../../demo/handler/handlers.h
../../demo/handler/s_dcc.o: ../../demo/handler/s_dcc.c \
../../config.h \
../../demo/handler/txbuf.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h \
../../demo/handler/handlers.h
../../bacdcode.o: ../../bacdcode.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../bits.h \
../../bigend.h
../../bacapp.o: ../../bacapp.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../bacapp.h \
../../bactext.h \
../../indtext.h
../../bacstr.o: ../../bacstr.c \
../../bacstr.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bits.h
../../bactext.o: ../../bactext.c \
../../indtext.h \
../../bacenum.h
../../indtext.o: ../../indtext.c \
../../indtext.h
../../bigend.o: ../../bigend.c
../../whois.o: ../../whois.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h
../../iam.o: ../../iam.c \
../../bacenum.h \
../../bacdef.h \
../../config.h \
../../npdu.h \
../../datalink.h \
../../ethernet.h \
../../arcnet.h \
../../dlmstp.h \
../../bip.h \
../../bacdcode.h \
../../bacstr.h \
../../address.h
../../rp.o: ../../rp.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../rp.h
../../wp.o: ../../wp.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../wp.h \
../../bacapp.h
../../arf.o: ../../arf.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../arf.h
../../awf.o: ../../awf.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../awf.h
../../dcc.o: ../../dcc.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h \
../../dcc.h
../../demo/object/bacfile.o: ../../demo/object/bacfile.c \
../../config.h \
../../address.h \
../../bacdef.h \
../../bacenum.h \
../../datalink.h \
../../ethernet.h \
../../arcnet.h \
../../dlmstp.h \
../../bip.h \
../../bacdcode.h \
../../bacstr.h \
../../npdu.h \
../../demo/object/device.h \
../../wp.h \
../../bacapp.h \
../../arf.h \
../../awf.h
../../demo/object/device.o: ../../demo/object/device.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../demo/object/ai.h \
../../demo/object/bi.h \
../../demo/object/bo.h \
../../wp.h \
../../bacapp.h \
../../demo/object/ao.h \
../../demo/object/lsp.h \
../../demo/object/device.h \
../../demo/object/bacfile.h \
../../arf.h
../../demo/object/ai.o: ../../demo/object/ai.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h
../../demo/object/ao.o: ../../demo/object/ao.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../wp.h \
../../bacapp.h
../../demo/object/bi.o: ../../demo/object/bi.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h
../../demo/object/bo.o: ../../demo/object/bo.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../wp.h \
../../bacapp.h
../../demo/object/lsp.o: ../../demo/object/lsp.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../wp.h \
../../bacapp.h
../../datalink.o: ../../datalink.c \
../../datalink.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../ethernet.h \
../../arcnet.h \
../../dlmstp.h \
../../bip.h
../../tsm.o: ../../tsm.c \
../../bits.h \
../../apdu.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../tsm.h \
../../demo/object/device.h \
../../wp.h \
../../bacapp.h \
../../datalink.h \
../../ethernet.h \
../../arcnet.h \
../../dlmstp.h \
../../bip.h \
../../demo/handler/handlers.h \
../../address.h
../../address.o: ../../address.c \
../../config.h \
../../address.h \
../../bacdef.h \
../../bacenum.h \
../../bacdcode.h \
../../bacstr.h
../../abort.o: ../../abort.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h
../../reject.o: ../../reject.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h
../../bacerror.o: ../../bacerror.c \
../../bacenum.h \
../../bacdcode.h \
../../bacdef.h \
../../config.h \
../../bacstr.h
../../apdu.o: ../../apdu.c \
../../bits.h \
../../apdu.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../tsm.h \
../../dcc.h \
../../iam.h
../../npdu.o: ../../npdu.c \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacdcode.h \
../../bacstr.h \
../../bits.h \
../../npdu.h \
../../apdu.h
../../ports/linux/bip-init.o: ../../ports/linux/bip-init.c \
../../bacdcode.h \
../../bacdef.h \
../../bacenum.h \
../../config.h \
../../bacstr.h \
../../bip.h \
../../ports/linux/net.h
+59
View File
@@ -0,0 +1,59 @@
TEMPLATE = app
CONFIG = warn_on debug console
CLEAN_FILES = core *~
TARGET = bacdcc
DEFINES = BACDL_BIP=1 TSM_ENABLED=1 USE_INADDR=1 BIP_DEBUG
SOURCES = main.c \
../../filename.c \
../../bip.c \
../../demo/handler/txbuf.c \
../../demo/handler/noserv.c \
../../demo/handler/h_whois.c \
../../demo/handler/h_iam.c \
../../demo/handler/h_rp.c \
../../demo/handler/h_dcc.c \
../../demo/handler/s_whois.c \
../../demo/handler/s_dcc.c \
../../bacdcode.c \
../../bacapp.c \
../../bacstr.c \
../../bactext.c \
../../indtext.c \
../../bigend.c \
../../whois.c \
../../iam.c \
../../rp.c \
../../wp.c \
../../arf.c \
../../awf.c \
../../dcc.c \
../../demo/object/bacfile.c \
../../demo/object/device.c \
../../demo/object/ai.c \
../../demo/object/ao.c \
../../demo/object/bi.c \
../../demo/object/bo.c \
../../demo/object/lsp.c \
../../datalink.c \
../../tsm.c \
../../address.c \
../../abort.c \
../../reject.c \
../../bacerror.c \
../../apdu.c \
../../npdu.c
unix:SOURCES += ../../ports/linux/bip-init.c
win32:SOURCES += ../../ports/win32/bip-init.c
INCLUDEPATH = . \
../../ \
../../demo/object \
../../demo/handler
unix:INCLUDEPATH += ../../ports/linux
win32:INCLUDEPATH += ../../ports/win32
#unix:HEADERS += ../../ports/linux/net.h
#win32:HEADERS += ../../ports/win32/stdint.h
#win32:HEADERS += ../../ports/win32/net.h
#win32:HEADERS += ../../ports/win32/stdbool.h
+90
View File
@@ -0,0 +1,90 @@
/**************************************************************************
*
* 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 CLIENT_H
#define CLIENT_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "bacdef.h"
#include "apdu.h"
#include "bacapp.h"
#include "bacenum.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* unconfirmed requests */
void Send_WhoIs(int32_t low_limit, int32_t high_limit);
void Send_WhoHas_Object(int32_t low_limit,
int32_t high_limit,
BACNET_OBJECT_TYPE object_type, uint32_t object_instance);
void Send_WhoHas_Name(int32_t low_limit,
int32_t high_limit, char *object_name);
void Send_I_Have(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance, char *object_name);
/* returns the invoke ID for confirmed request, or 0 if failed */
uint8_t Send_Read_Property_Request(uint32_t device_id, /* destination device */
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property, int32_t array_index);
/* returns the invoke ID for confirmed request, or 0 if failed */
uint8_t Send_Write_Property_Request(uint32_t device_id, /* destination device */
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE * object_value,
uint8_t priority, int32_t array_index);
/* returns the invoke ID for confirmed request, or 0 if failed */
uint8_t Send_Reinitialize_Device_Request(uint32_t device_id,
BACNET_REINITIALIZED_STATE state, char *password);
/* returns the invoke ID for confirmed request, or 0 if failed */
uint8_t Send_Device_Communication_Control_Request(uint32_t device_id, uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE state, char *password); /* NULL=optional */
void Send_TimeSync(BACNET_DATE * bdate, BACNET_TIME * btime);
void Send_TimeSyncUTC(BACNET_DATE * bdate, BACNET_TIME * btime);
uint8_t Send_Atomic_Read_File_Stream(uint32_t device_id,
uint32_t file_instance, int fileStartPosition,
unsigned requestedOctetCount);
uint8_t Send_Atomic_Write_File_Stream(uint32_t device_id,
uint32_t file_instance,
int fileStartPosition, BACNET_OCTET_STRING * fileData);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+185
View File
@@ -0,0 +1,185 @@
/**************************************************************************
*
* 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 <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "arf.h"
/* demo objects */
#include "device.h"
#include "ai.h"
#include "ao.h"
#include "bacfile.h"
/*
from BACnet SSPC-135-2004
14. FILE ACCESS SERVICES
This clause defines the set of services used to access and
manipulate files contained in BACnet devices. The concept of files
is used here as a network-visible representation for a collection
of octets of arbitrary length and meaning. This is an abstract
concept only and does not imply the use of disk, tape or other
mass storage devices in the server devices. These services may
be used to access vendor-defined files as well as specific
files defined in the BACnet protocol standard.
Every file that is accessible by File Access Services shall
have a corresponding File object in the BACnet device. This File
object is used to identify the particular file by name. In addition,
the File object provides access to "header information," such
as the file's total size, creation date, and type. File Access
Services may model files in two ways: as a continuous stream of
octets or as a contiguous sequence of numbered records.
The File Access Services provide atomic read and write operations.
In this context "atomic" means that during the execution
of a read or write operation, no other AtomicReadFile or
AtomicWriteFile operations are allowed for the same file.
Synchronization of these services with internal operations
of the BACnet device is a local matter and is not defined by this
standard.
14.1 AtomicReadFile Service
14.1.5 Service Procedure
The responding BACnet-user shall first verify the validity
of the 'File Identifier' parameter and return a 'Result(-)' response
with the appropriate error class and code if the File object
is unknown, if there is currently another AtomicReadFile or
AtomicWriteFile service in progress, or if the File object is
currently inaccessible for another reason. If the 'File Start
Position' parameter or the 'File Start Record' parameter is
either less than 0 or exceeds the actual file size, then the appropriate
error is returned in a 'Result(-)' response. If not, then the
responding BACnet-user shall read the number of octets specified by
'Requested Octet Count' or the number of records specified by
'Requested Record Count'. If the number of remaining octets or
records is less than the requested amount, then the length of
the 'File Data' returned or 'Returned Record Count' shall indicate
the actual number read. If the returned response contains the
last octet or record of the file, then the 'End Of File' parameter
shall be TRUE, otherwise FALSE.
*/
void handler_atomic_read_file(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_ATOMIC_READ_FILE_DATA data;
int len = 0;
int pdu_len = 0;
bool error = false;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
#if PRINT_ENABLED
fprintf(stderr, "Received Atomic-Read-File Request!\n");
#endif
len = arf_decode_service_request(service_request, service_len, &data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
/* bad decoding - send an abort */
if (len < 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Bad Encoding. Sending Abort!\n");
#endif
} else if (service_data->segmented_message) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "Segmented Message. Sending Abort!\n");
#endif
} else if (data.object_type == OBJECT_FILE) {
if (!bacfile_valid_instance(data.object_instance)) {
error = true;
} else if (data.access == FILE_STREAM_ACCESS) {
if (data.type.stream.requestedOctetCount <
octetstring_capacity(&data.fileData)) {
if (bacfile_read_data(&data)) {
len =
arf_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
} else {
error = true;
error_class = ERROR_CLASS_OBJECT;
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
}
} else {
len =
abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "Too Big To Send. Sending Abort!\n");
#endif
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_INVALID_FILE_ACCESS_METHOD;
#if PRINT_ENABLED
fprintf(stderr, "Record Access Requested. Sending Error!\n");
#endif
}
} else {
error = true;
error_class = ERROR_CLASS_SERVICES;
error_code = ERROR_CODE_FILE_ACCESS_DENIED;
}
if (error) {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_ATOMIC_READ_FILE, error_class, error_code);
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#endif
return;
}
+93
View File
@@ -0,0 +1,93 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "arf.h"
#include "bacfile.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* We performed an AtomicReadFile Request, */
/* and here is the data from the server */
/* Note: it does not have to be the same file=instance */
/* that someone can read from us. It is common to */
/* use the description as the file name. */
#if BACFILE
void handler_atomic_read_file_ack(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_ATOMIC_READ_FILE_DATA data;
FILE *pFile = NULL;
char *pFilename = NULL;
uint32_t instance = 0;
(void) src;
/* get the file instance from the tsm data before freeing it */
instance = bacfile_instance_from_tsm(service_data->invoke_id);
len = arf_ack_decode_service_request(service_request,
service_len, &data);
#if PRINT_ENABLED
fprintf(stderr, "Received Read-File Ack!\n");
#endif
if ((len > 0) && (instance <= BACNET_MAX_INSTANCE)) {
/* write the data received to the file specified */
if (data.access == FILE_STREAM_ACCESS) {
pFilename = bacfile_name(instance);
if (pFilename) {
pFile = fopen(pFilename, "rb");
if (pFile) {
(void) fseek(pFile,
data.type.stream.fileStartPosition, SEEK_SET);
if (fwrite(octetstring_value(&data.fileData),
octetstring_length(&data.fileData), 1,
pFile) != 1) {
#if PRINT_ENABLED
fprintf(stderr, "Failed to write to %s (%u)!\n",
pFilename, instance);
#endif
}
fclose(pFile);
}
}
} else if (data.access == FILE_RECORD_ACCESS) {
/* FIXME: add handling for Record Access */
}
}
}
#endif
+128
View File
@@ -0,0 +1,128 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "reject.h"
#include "dcc.h"
static char *My_Password = "filister";
void handler_device_communication_control(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
uint16_t timeDuration = 0;
BACNET_COMMUNICATION_ENABLE_DISABLE state = COMMUNICATION_ENABLE;
BACNET_CHARACTER_STRING password;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
/* decode the service request only */
len = dcc_decode_service_request(service_request,
service_len, &timeDuration, &state, &password);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
#if PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl!\n");
if (len > 0)
fprintf(stderr, "DeviceCommunicationControl: "
"timeout=%u state=%u password=%s\n",
(unsigned) timeDuration,
(unsigned) state, characterstring_value(&password));
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len < 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl: "
"Sending Abort - could not decode.\n");
#endif
} else if (service_data->segmented_message) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl: "
"Sending Abort - segmented message.\n");
#endif
} else if (state >= MAX_BACNET_COMMUNICATION_ENABLE_DISABLE) {
len = reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
#if PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl: "
"Sending Reject - undefined enumeration\n");
#endif
} else {
if (characterstring_ansi_same(&password, My_Password)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL);
#if PRINT_ENABLED
fprintf(stderr, "DeviceCommunicationControl: "
"Sending Simple Ack!\n");
#endif
dcc_set_status_duration(state, timeDuration);
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL,
ERROR_CLASS_SERVICES, ERROR_CODE_PASSWORD_FAILURE);
#if PRINT_ENABLED
fprintf(stderr,
"DeviceCommunicationControl: "
"Sending Error - password failure.\n");
#endif
}
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "DeviceCommunicationControl: "
"Failed to send PDU (%s)!\n", strerror(errno));
#endif
return;
}
+80
View File
@@ -0,0 +1,80 @@
/**************************************************************************
*
* 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 "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "iam.h"
#include "address.h"
void handler_i_am_add(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) service_len;
len = iam_decode_service_request(service_request,
&device_id, &max_apdu, &segmentation, &vendor_id);
#if PRINT_ENABLED
fprintf(stderr, "Received I-Am Request");
#endif
if (len != -1) {
#if PRINT_ENABLED
fprintf(stderr, " from %u!\n", device_id);
#endif
address_add(device_id, max_apdu, src);
} else {
#if PRINT_ENABLED
fprintf(stderr, "!\n");
#endif
}
return;
}
void handler_i_am_bind(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
uint32_t device_id = 0;
unsigned max_apdu = 0;
int segmentation = 0;
uint16_t vendor_id = 0;
(void) service_len;
len = iam_decode_service_request(service_request,
&device_id, &max_apdu, &segmentation, &vendor_id);
/* only add address if requested to bind */
address_add_binding(device_id, max_apdu, src);
return;
}
+60
View File
@@ -0,0 +1,60 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bactext.h"
#include "ihave.h"
void handler_i_have(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
BACNET_I_HAVE_DATA data;
(void) service_len;
(void) src;
len = ihave_decode_service_request(service_request,
service_len, &data);
if (len != -1) {
#if PRINT_ENABLED
fprintf(stderr, "I-Have: %s %d from %s %u!\r\n",
bactext_object_type_name(data.object_id.type),
data.object_id.instance,
bactext_object_type_name(data.device_id.type),
data.device_id.instance);
#endif
} else {
#if PRINT_ENABLED
fprintf(stderr, "I-Have: received, but unable to decode!\n");
#endif
}
return;
}
+130
View File
@@ -0,0 +1,130 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "reject.h"
#include "rd.h"
static char *Password = "Jesus";
static BACNET_CHARACTER_STRING My_Password;
void handler_reinitialize_device(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_REINITIALIZED_STATE state;
BACNET_CHARACTER_STRING their_password;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* decode the service request only */
len = rd_decode_service_request(service_request,
service_len, &state, &their_password);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
#if PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice!\n");
if (len > 0)
fprintf(stderr, "ReinitializeDevice: state=%u password=%s\n",
(unsigned) state, characterstring_value(&their_password));
else
fprintf(stderr, "ReinitializeDevice: Unable to decode request!\n");
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len < 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Abort - could not decode.\n");
#endif
} else if (service_data->segmented_message) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Abort - segmented message.\n");
#endif
} else if (state >= MAX_BACNET_REINITIALIZED_STATE) {
len = reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNDEFINED_ENUMERATION);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Reject - undefined enumeration\n");
#endif
} else {
characterstring_init_ansi(&My_Password, Password);
if (characterstring_same(&their_password, &My_Password)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_REINITIALIZE_DEVICE);
#if PRINT_ENABLED
fprintf(stderr, "ReinitializeDevice: Sending Simple Ack!\n");
#endif
/* FIXME: now you can reboot, restart, quit, or something clever */
/* Note: you can use a mix of state and password to do specific stuff */
/* Note: if you don't do something clever like actually restart,
you probably should clear any DCC status and timeouts */
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_REINITIALIZE_DEVICE,
ERROR_CLASS_SERVICES, ERROR_CODE_PASSWORD_FAILURE);
#if PRINT_ENABLED
fprintf(stderr,
"ReinitializeDevice: Sending Error - password failure.\n");
#endif
}
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "ReinitializeDevice: Failed to send PDU (%s)!\n",
strerror(errno));
#endif
return;
}
+368
View File
@@ -0,0 +1,368 @@
/**************************************************************************
*
* 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 <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "rp.h"
/* demo objects */
#include "device.h"
#include "ai.h"
#include "ao.h"
#include "av.h"
#include "bi.h"
#include "bo.h"
#include "bv.h"
#include "lc.h"
#include "lsp.h"
#include "mso.h"
#if BACFILE
#include "bacfile.h"
#endif
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
void handler_read_property(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_READ_PROPERTY_DATA data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
bool error = false;
int bytes_sent = 0;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
BACNET_ADDRESS my_address;
len = rp_decode_service_request(service_request, service_len, &data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
#if PRINT_ENABLED
if (len <= 0)
fprintf(stderr, "Unable to decode Read-Property Request!\n");
#endif
if (len < 0) {
/* bad decoding - send an abort */
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Sending Abort!\n");
#endif
} else if (service_data->segmented_message) {
/* we don't support segmentation - send an abort */
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "Sending Abort!\n");
#endif
} else {
/* most cases will be error */
error = true;
switch (data.object_type) {
case OBJECT_DEVICE:
/* FIXME: probably need a length limitation sent with encode */
if (data.object_instance == Device_Object_Instance_Number()) {
len = Device_Encode_Property_APDU(&Temp_Buf[0],
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Read Property Ack for Device!\n");
#endif
error = false;
}
}
break;
case OBJECT_ANALOG_INPUT:
if (Analog_Input_Valid_Instance(data.object_instance)) {
len = Analog_Input_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for AI!\n");
#endif
error = false;
}
}
break;
case OBJECT_BINARY_INPUT:
if (Binary_Input_Valid_Instance(data.object_instance)) {
len = Binary_Input_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for BI!\n");
#endif
error = false;
}
}
break;
case OBJECT_BINARY_OUTPUT:
if (Binary_Output_Valid_Instance(data.object_instance)) {
len = Binary_Output_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for BO!\n");
#endif
error = false;
}
}
break;
case OBJECT_BINARY_VALUE:
if (Binary_Value_Valid_Instance(data.object_instance)) {
len = Binary_Value_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for BV!\n");
#endif
error = false;
}
}
break;
case OBJECT_ANALOG_OUTPUT:
if (Analog_Output_Valid_Instance(data.object_instance)) {
len = Analog_Output_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for AO!\n");
#endif
error = false;
}
}
break;
case OBJECT_ANALOG_VALUE:
if (Analog_Value_Valid_Instance(data.object_instance)) {
len = Analog_Value_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Ack for AV!\n");
#endif
error = false;
}
}
break;
case OBJECT_LIFE_SAFETY_POINT:
if (Life_Safety_Point_Valid_Instance(data.object_instance)) {
len = Life_Safety_Point_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Read Property Ack for LSP!\n");
#endif
error = false;
}
}
break;
case OBJECT_LOAD_CONTROL:
if (Load_Control_Valid_Instance(data.object_instance)) {
len = Load_Control_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Read Property Ack for Load Control!\n");
#endif
error = false;
}
}
break;
case OBJECT_MULTI_STATE_OUTPUT:
if (Multistate_Output_Valid_Instance(data.object_instance)) {
len = Multistate_Output_Encode_Property_APDU(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Read Property Ack for MSO!\n");
#endif
error = false;
}
}
break;
#if BACFILE
case OBJECT_FILE:
if (bacfile_valid_instance(data.object_instance)) {
len = bacfile_encode_property_apdu(&Temp_Buf[0],
data.object_instance,
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Read Property Ack for File!\n");
#endif
error = false;
}
}
break;
#endif /* BACFILE */
default:
break;
}
}
if (error) {
switch (len) {
/* BACnet APDU too small to fit data, so proper response is Abort */
case -2:
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
break;
case -1:
default:
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
break;
}
#if PRINT_ENABLED
fprintf(stderr, "Sending Read Property Error!\n");
#endif
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
#endif
return;
}
+114
View File
@@ -0,0 +1,114 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "bactext.h"
#include "rp.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* for debugging... */
static void PrintReadPropertyData(BACNET_READ_PROPERTY_DATA * data)
{
BACNET_APPLICATION_DATA_VALUE value; /* for decode value data */
int len = 0;
uint8_t *application_data;
int application_data_len;
bool first_value = true;
bool print_brace = false;
if (data) {
#if 0
if (data->array_index == BACNET_ARRAY_ALL)
fprintf(stderr, "%s #%u %s\n",
bactext_object_type_name(data->object_type),
data->object_instance,
bactext_property_name(data->object_property));
else
fprintf(stderr, "%s #%u %s[%d]\n",
bactext_object_type_name(data->object_type),
data->object_instance,
bactext_property_name(data->object_property),
data->array_index);
#endif
application_data = data->application_data;
application_data_len = data->application_data_len;
/* FIXME: what if application_data_len is bigger than 255? */
/* value? need to loop until all of the len is gone... */
for (;;) {
len = bacapp_decode_application_data(application_data,
(uint8_t) application_data_len, &value);
if (first_value && (len < application_data_len)) {
first_value = false;
fprintf(stdout, "{");
print_brace = true;
}
bacapp_print_value(stdout, &value, data->object_property);
if (len) {
if (len < application_data_len) {
application_data += len;
application_data_len -= len;
/* there's more! */
fprintf(stdout, ",");
} else
break;
} else
break;
}
if (print_brace)
fprintf(stdout, "}");
fprintf(stdout, "\r\n");
}
}
void handler_read_property_ack(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
int len = 0;
BACNET_READ_PROPERTY_DATA data;
(void) src;
(void) service_data; /* we could use these... */
len = rp_ack_decode_service_request(service_request,
service_len, &data);
#if 0
fprintf(stderr, "Received Read-Property Ack!\n");
#endif
if (len > 0)
PrintReadPropertyData(&data);
}
+122
View File
@@ -0,0 +1,122 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "bacerror.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "rp.h"
/* demo objects */
#include "device.h"
/* note: this is a minimal handler. See h_rp.c for another */
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
void handler_read_property(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_READ_PROPERTY_DATA data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
bool send = false;
bool error = false;
int bytes_sent = 0;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
BACNET_ADDRESS my_address;
len = rp_decode_service_request(service_request, service_len, &data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
if (len < 0) {
/* bad decoding - send an abort */
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
} else if (service_data->segmented_message) {
/* we don't support segmentation - send an abort */
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
} else {
/* most cases will be error */
error = true;
switch (data.object_type) {
case OBJECT_DEVICE:
/* FIXME: probably need a length limitation sent with encode */
if (data.object_instance == Device_Object_Instance_Number()) {
len = Device_Encode_Property_APDU(&Temp_Buf[0],
data.object_property,
data.array_index, &error_class, &error_code);
if (len >= 0) {
/* encode the APDU portion of the packet */
data.application_data = &Temp_Buf[0];
data.application_data_len = len;
/* FIXME: probably need a length limitation sent with encode */
len =
rp_ack_encode_apdu(&Handler_Transmit_Buffer
[pdu_len], service_data->invoke_id, &data);
error = false;
}
}
break;
default:
break;
}
}
if (error) {
switch (len) {
/* BACnet APDU too small to fit data, so proper response is Abort */
case -2:
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
break;
case -1:
default:
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code);
break;
}
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
return;
}
+82
View File
@@ -0,0 +1,82 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "timesync.h"
static void show_bacnet_date_time(BACNET_DATE * bdate, BACNET_TIME * btime)
{
/* show the date received */
fprintf(stderr, "%u", (unsigned) bdate->year);
fprintf(stderr, "/%u", (unsigned) bdate->month);
fprintf(stderr, "/%u", (unsigned) bdate->day);
/* show the time received */
fprintf(stderr, " %02u", (unsigned) btime->hour);
fprintf(stderr, ":%02u", (unsigned) btime->min);
fprintf(stderr, ":%02u", (unsigned) btime->sec);
fprintf(stderr, ".%02u", (unsigned) btime->hundredths);
fprintf(stderr, "\r\n");
}
void handler_timesync(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
BACNET_DATE bdate;
BACNET_TIME btime;
(void) src;
(void) service_len;
len = timesync_decode_service_request(service_request,
service_len, &bdate, &btime);
fprintf(stderr, "Received TimeSyncronization Request\r\n");
show_bacnet_date_time(&bdate, &btime);
/* FIXME: set the time? */
return;
}
void handler_timesync_utc(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
BACNET_DATE bdate;
BACNET_TIME btime;
(void) src;
(void) service_len;
len = timesync_decode_service_request(service_request,
service_len, &bdate, &btime);
fprintf(stderr, "Received TimeSyncronization Request\r\n");
show_bacnet_date_time(&bdate, &btime);
/* FIXME: set the time? */
return;
}
@@ -0,0 +1,85 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "whohas.h"
#include "device.h"
#include "client.h"
void handler_who_has(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
BACNET_WHO_HAS_DATA data;
bool directed_to_me = false;
int object_type = 0;
uint32_t object_instance = 0;
char *object_name = NULL;
bool found = false;
(void) src;
len = whohas_decode_service_request(service_request,
service_len, &data);
if (len > 0) {
if ((data.low_limit == -1) || (data.high_limit == -1))
directed_to_me = true;
else if ((Device_Object_Instance_Number() >=
(uint32_t) data.low_limit)
&& (Device_Object_Instance_Number() <=
(uint32_t) data.high_limit))
directed_to_me = true;
if (directed_to_me) {
/* do we have such an object? If so, send an I-Have.
note: we should have only 1 of such an object */
if (data.object_name) {
/* valid name in my device? */
object_name = characterstring_value(&data.object.name);
found = Device_Valid_Object_Name(object_name,
&object_type, &object_instance);
if (found)
Send_I_Have(Device_Object_Instance_Number(),
object_type, object_instance, object_name);
} else {
/* valid object in my device? */
object_name =
Device_Valid_Object_Id(data.object.identifier.type,
data.object.identifier.instance);
if (object_name)
Send_I_Have(Device_Object_Instance_Number(),
data.object.identifier.type,
data.object.identifier.instance, object_name);
}
}
}
return;
}
+66
View File
@@ -0,0 +1,66 @@
/**************************************************************************
*
* 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 <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "whois.h"
#include "iam.h"
#include "device.h"
#include "client.h"
#include "txbuf.h"
void handler_who_is(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src)
{
int len = 0;
int32_t low_limit = 0;
int32_t high_limit = 0;
(void) src;
len = whois_decode_service_request(service_request,
service_len, &low_limit, &high_limit);
/* in our simple system, we just set a flag and let the main loop
send the I-Am request. */
if (len == 0)
iam_send(&Handler_Transmit_Buffer[0]);
else if (len != -1) {
/* is my device id within the limits? */
if (((Device_Object_Instance_Number() >= (uint32_t) low_limit) &&
(Device_Object_Instance_Number() <= (uint32_t) high_limit))
||
/* BACnet wildcard is the max instance number - everyone responds */
((BACNET_MAX_INSTANCE >= (uint32_t) low_limit) &&
(BACNET_MAX_INSTANCE <= (uint32_t) high_limit)))
iam_send(&Handler_Transmit_Buffer[0]);
}
return;
}
+333
View File
@@ -0,0 +1,333 @@
/**************************************************************************
*
* 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 <string.h>
#include <errno.h>
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "abort.h"
#include "wp.h"
/* demo objects */
#include "device.h"
#include "ai.h"
#include "ao.h"
#include "av.h"
#include "bi.h"
#include "bo.h"
#include "bv.h"
#include "lc.h"
#include "lsp.h"
#include "mso.h"
#if BACFILE
#include "bacfile.h"
#endif
void handler_write_property(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
BACNET_WRITE_PROPERTY_DATA wp_data;
int len = 0;
int pdu_len = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_OBJECT;
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
int bytes_sent = 0;
BACNET_ADDRESS my_address;
/* decode the service request only */
len = wp_decode_service_request(service_request,
service_len, &wp_data);
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
#if PRINT_ENABLED
fprintf(stderr, "Received Write-Property Request!\n");
if (len > 0)
fprintf(stderr, "type=%u instance=%u property=%u index=%d\n",
wp_data.object_type,
wp_data.object_instance,
wp_data.object_property, wp_data.array_index);
else
fprintf(stderr, "Unable to decode Write-Property Request!\n");
#endif
/* bad decoding or something we didn't understand - send an abort */
if (len <= 0) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, ABORT_REASON_OTHER, true);
#if PRINT_ENABLED
fprintf(stderr, "Sending Abort!\n");
#endif
} else if (service_data->segmented_message) {
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
#if PRINT_ENABLED
fprintf(stderr, "Sending Abort!\n");
#endif
} else {
switch (wp_data.object_type) {
case OBJECT_DEVICE:
if (Device_Write_Property(&wp_data, &error_class, &error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for Device!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Error for Device!\n");
#endif
}
break;
case OBJECT_ANALOG_INPUT:
case OBJECT_BINARY_INPUT:
error_class = ERROR_CLASS_PROPERTY;
error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
error_class, error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error!\n");
#endif
break;
case OBJECT_BINARY_OUTPUT:
if (Binary_Output_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for BO!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for BO!\n");
#endif
}
break;
case OBJECT_BINARY_VALUE:
if (Binary_Value_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for BV!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for BV!\n");
#endif
}
break;
case OBJECT_ANALOG_OUTPUT:
if (Analog_Output_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for AO!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for AO!\n");
#endif
}
break;
case OBJECT_ANALOG_VALUE:
if (Analog_Value_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for AV!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for AV!\n");
#endif
}
break;
case OBJECT_LIFE_SAFETY_POINT:
if (Life_Safety_Point_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for LSP!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for LSP!\n");
#endif
}
break;
case OBJECT_LOAD_CONTROL:
if (Load_Control_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for Load Control!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Access Error for Load Control!\n");
#endif
}
break;
case OBJECT_MULTI_STATE_OUTPUT:
if (Multistate_Output_Write_Property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for MSO!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for MSO!\n");
#endif
}
break;
#if BACFILE
case OBJECT_FILE:
if (bacfile_write_property(&wp_data, &error_class,
&error_code)) {
len =
encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY);
#if PRINT_ENABLED
fprintf(stderr,
"Sending Write Property Simple Ack for File!\n");
#endif
} else {
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_WRITE_PROPERTY, error_class,
error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Write Access Error for File!\n");
#endif
}
break;
#endif /* BACFILE */
default:
len =
bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_WRITE_PROPERTY,
error_class, error_code);
#if PRINT_ENABLED
fprintf(stderr, "Sending Unknown Object Error!\n");
#endif
break;
}
}
pdu_len += len;
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
#endif
return;
}
+103
View File
@@ -0,0 +1,103 @@
/**************************************************************************
*
* Copyright (C) 2005-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 HANDLERS_H
#define HANDLERS_H
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "bacdef.h"
#include "apdu.h"
#include "bacapp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
void handler_unrecognized_service(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * dest,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_who_is(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_who_has(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_i_am_add(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_i_am_bind(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_read_property(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_read_property_ack(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
void handler_write_property(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_atomic_read_file(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_atomic_read_file_ack(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data);
void handler_reinitialize_device(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_device_communication_control(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA * service_data);
void handler_i_have(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_timesync(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
void handler_timesync_utc(uint8_t * service_request,
uint16_t service_len, BACNET_ADDRESS * src);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+68
View File
@@ -0,0 +1,68 @@
/**************************************************************************
*
* 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 <string.h>
#include <errno.h>
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "apdu.h"
#include "npdu.h"
#include "reject.h"
void handler_unrecognized_service(uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data)
{
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
(void) service_request;
(void) service_len;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], src,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len = reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE);
pdu_len += len;
/* send the data */
bytes_sent = datalink_send_pdu(src, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent > 0)
fprintf(stderr, "Sent Reject!\n");
else
fprintf(stderr, "Failed to Send Reject (%s)!\n", strerror(errno));
#endif
}
+112
View File
@@ -0,0 +1,112 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "dcc.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "arf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
uint8_t Send_Atomic_Read_File_Stream(uint32_t device_id,
uint32_t file_instance,
int fileStartPosition, unsigned requestedOctetCount)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_ATOMIC_READ_FILE_DATA data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* load the data for the encoding */
data.object_type = OBJECT_FILE;
data.object_instance = file_instance;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = fileStartPosition;
data.type.stream.requestedOctetCount = requestedOctetCount;
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
len = arf_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, &data);
pdu_len += len;
/* will the APDU fit the target device?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send AtomicReadFile Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send AtomicReadFile Request "
"(payload exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+126
View File
@@ -0,0 +1,126 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "dcc.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "awf.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
uint8_t Send_Atomic_Write_File_Stream(uint32_t device_id,
uint32_t file_instance,
int fileStartPosition, BACNET_OCTET_STRING * fileData)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
BACNET_NPDU_DATA npdu_data;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_ATOMIC_WRITE_FILE_DATA data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* load the data for the encoding */
data.object_type = OBJECT_FILE;
data.object_instance = file_instance;
data.access = FILE_STREAM_ACCESS;
data.type.stream.fileStartPosition = fileStartPosition;
status = octetstring_copy(&data.fileData, fileData);
if (status) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true,
MESSAGE_PRIORITY_NORMAL);
pdu_len =
npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
len = awf_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, &data);
pdu_len += len;
/* will the APDU fit the target device?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len <= max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send AtomicWriteFile Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send AtomicWriteFile Request "
"(payload [%d] exceeds destination maximum APDU [%u])!\n",
pdu_len, max_apdu);
#endif
}
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send AtomicWriteFile Request "
"(payload [%d] exceeds octet string capacity)!\n",
pdu_len);
#endif
}
}
return invoke_id;
}
+109
View File
@@ -0,0 +1,109 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
uint8_t Send_Device_Communication_Control_Request(uint32_t device_id, uint16_t timeDuration, /* 0=optional */
BACNET_COMMUNICATION_ENABLE_DISABLE state, char *password)
{ /* NULL=optional */
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_CHARACTER_STRING password_string;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
characterstring_init_ansi(&password_string, password);
len = dcc_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id,
timeDuration, state, password ? &password_string : NULL);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send DeviceCommunicationControl Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr,
"Failed to Send DeviceCommunicationControl Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+83
View File
@@ -0,0 +1,83 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "ihave.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* find a specific device, or use -1 for limit if you want unlimited */
void Send_I_Have(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance, char *object_name)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_I_HAVE_DATA data;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
NULL, &npdu_data);
/* encode the APDU portion of the packet */
data.device_id.type = OBJECT_DEVICE;
data.device_id.instance = device_id;
data.object_id.type = object_type;
data.object_id.instance = object_instance;
characterstring_init_ansi(&data.object_name, object_name);
len = ihave_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
/* send the data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send I-Have Reply (%s)!\n",
strerror(errno));
#endif
}
+107
View File
@@ -0,0 +1,107 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rd.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
uint8_t Send_Reinitialize_Device_Request(uint32_t device_id,
BACNET_REINITIALIZED_STATE state, char *password)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_CHARACTER_STRING password_string;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
characterstring_init_ansi(&password_string, password);
len = rd_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, state, password ? &password_string : NULL);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send ReinitializeDevice Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send ReinitializeDevice Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+114
View File
@@ -0,0 +1,114 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "rp.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* returns invoke id of 0 if device is not bound or no tsm available */
uint8_t Send_Read_Property_Request(uint32_t device_id, /* destination device */
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property, int32_t array_index)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_READ_PROPERTY_DATA data;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
data.object_type = object_type;
data.object_instance = object_instance;
data.object_property = object_property;
data.array_index = array_index;
len = rp_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, &data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0],
(uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send ReadProperty Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send ReadProperty Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
+100
View File
@@ -0,0 +1,100 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "timesync.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
void Send_TimeSync(BACNET_DATE * bdate, BACNET_TIME * btime)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return;
/* we could use unicast or broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
NULL, &npdu_data);
/* encode the APDU portion of the packet */
len = timesync_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
bdate, btime);
pdu_len += len;
/* send it out the datalink */
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send Time-Synchronization Request (%s)!\n",
strerror(errno));
#endif
}
void Send_TimeSyncUTC(BACNET_DATE * bdate, BACNET_TIME * btime)
{
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return;
/* we could use unicast or broadcast */
datalink_get_broadcast_address(&dest);
/* encode the APDU portion of the packet */
pdu_len = timesync_utc_encode_apdu(&Handler_Transmit_Buffer[0],
bdate, btime);
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send UTC-Time-Synchronization Request (%s)!\n",
strerror(errno));
#endif
}
+118
View File
@@ -0,0 +1,118 @@
/**************************************************************************
*
* 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.
*
*********************************************************************/
#include <stddef.h>
#include <stdint.h>
#include <errno.h>
#include "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whohas.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* find a specific device, or use -1 for limit if you want unlimited */
void Send_WhoHas_Name(int32_t low_limit,
int32_t high_limit, char *object_name)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_WHO_HAS_DATA data;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
NULL, &npdu_data);
/* encode the APDU portion of the packet */
data.low_limit = low_limit;
data.high_limit = high_limit;
data.object_name = true;
characterstring_init_ansi(&data.object.name, object_name);
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
/* send the data */
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n",
strerror(errno));
#endif
}
/* find a specific device, or use -1 for limit if you want unlimited */
void Send_WhoHas_Object(int32_t low_limit,
int32_t high_limit,
BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_WHO_HAS_DATA data;
BACNET_NPDU_DATA npdu_data;
/* if we are forbidden to send, don't send! */
if (!dcc_communication_enabled())
return;
/* Who-Has is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
NULL, &npdu_data);
/* encode the APDU portion of the packet */
data.low_limit = low_limit;
data.high_limit = high_limit;
data.object_name = false;
data.object.identifier.type = object_type;
data.object.identifier.instance = object_instance;
len = whohas_encode_apdu(&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Has Request (%s)!\n",
strerror(errno));
#endif
}
+75
View File
@@ -0,0 +1,75 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* find a specific device, or use -1 for limit if you want unlimited */
void Send_WhoIs(int32_t low_limit, int32_t high_limit)
{
int len = 0;
int pdu_len = 0;
BACNET_ADDRESS dest;
int bytes_sent = 0;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return;
/* Who-Is is a global broadcast */
datalink_get_broadcast_address(&dest);
/* encode the NPDU portion of the packet */
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
NULL, &npdu_data);
/* encode the APDU portion of the packet */
len = whois_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
low_limit, high_limit);
pdu_len += len;
bytes_sent = datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr, "Failed to Send Who-Is Request (%s)!\n",
strerror(errno));
#endif
}
+148
View File
@@ -0,0 +1,148 @@
/**************************************************************************
*
* 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 "config.h"
#include "config.h"
#include "txbuf.h"
#include "bacdef.h"
#include "bacdcode.h"
#include "address.h"
#include "tsm.h"
#include "npdu.h"
#include "apdu.h"
#include "device.h"
#include "datalink.h"
#include "dcc.h"
#include "whois.h"
/* some demo stuff needed */
#include "handlers.h"
#include "txbuf.h"
/* returns the invoke ID for confirmed request, or zero on failure */
uint8_t Send_Write_Property_Request_Data(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t * application_data,
int application_data_len, uint8_t priority, int32_t array_index)
{
BACNET_ADDRESS dest;
BACNET_ADDRESS my_address;
unsigned max_apdu = 0;
uint8_t invoke_id = 0;
bool status = false;
int len = 0;
int pdu_len = 0;
int bytes_sent = 0;
BACNET_WRITE_PROPERTY_DATA data;
BACNET_NPDU_DATA npdu_data;
if (!dcc_communication_enabled())
return 0;
/* is the device bound? */
status = address_get_by_device(device_id, &max_apdu, &dest);
/* is there a tsm available? */
if (status)
invoke_id = tsm_next_free_invokeID();
if (invoke_id) {
/* encode the NPDU portion of the packet */
datalink_get_my_address(&my_address);
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
pdu_len = npdu_encode_pdu(&Handler_Transmit_Buffer[0], &dest,
&my_address, &npdu_data);
/* encode the APDU portion of the packet */
data.object_type = object_type;
data.object_instance = object_instance;
data.object_property = object_property;
data.array_index = array_index;
data.application_data_len = application_data_len;
memcpy(&data.application_data[0], &application_data[0],
application_data_len);
data.priority = priority;
len = wp_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
invoke_id, &data);
pdu_len += len;
/* will it fit in the sender?
note: if there is a bottleneck router in between
us and the destination, we won't know unless
we have a way to check for that and update the
max_apdu in the address binding table. */
if ((unsigned) pdu_len < max_apdu) {
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
&npdu_data, &Handler_Transmit_Buffer[0],
(uint16_t) pdu_len);
bytes_sent =
datalink_send_pdu(&dest, &npdu_data,
&Handler_Transmit_Buffer[0], pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0)
fprintf(stderr,
"Failed to Send WriteProperty Request (%s)!\n",
strerror(errno));
#endif
} else {
tsm_free_invoke_id(invoke_id);
invoke_id = 0;
#if PRINT_ENABLED
fprintf(stderr, "Failed to Send WriteProperty Request "
"(exceeds destination maximum APDU)!\n");
#endif
}
}
return invoke_id;
}
uint8_t Send_Write_Property_Request(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE * object_value,
uint8_t priority, int32_t array_index)
{
uint8_t application_data[MAX_APDU] = { 0 };
int apdu_len = 0, len = 0;
while (object_value) {
len = bacapp_encode_data(&application_data[apdu_len],
object_value);
if ((len + apdu_len) < MAX_APDU) {
apdu_len += len;
} else {
return 0;
}
object_value = object_value->next;
}
return Send_Write_Property_Request_Data(device_id,
object_type,
object_instance,
object_property,
&application_data[0], apdu_len, priority, array_index);
}
+30
View File
@@ -0,0 +1,30 @@
/**************************************************************************
*
* 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 "config.h"
#include "datalink.h"
uint8_t Handler_Transmit_Buffer[MAX_MPDU] = { 0 };
+35
View File
@@ -0,0 +1,35 @@
/**************************************************************************
*
* 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 TXBUF_H
#define TXBUF_H
#include <stddef.h>
#include <stdint.h>
#include "config.h"
#include "datalink.h"
extern uint8_t Handler_Transmit_Buffer[MAX_MPDU];
#endif
+197
View File
@@ -0,0 +1,197 @@
/**************************************************************************
*
* 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 Input Objects customize for your use */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "bacdef.h"
#include "bacdcode.h"
#include "bacenum.h"
#include "config.h" /* the custom stuff */
#define MAX_ANALOG_INPUTS 7
/* we simply have 0-n object instances. Yours might be */
/* more complex, and then you need validate that the */
/* given instance exists */
bool Analog_Input_Valid_Instance(uint32_t object_instance)
{
if (object_instance < MAX_ANALOG_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 Analog_Input_Count(void)
{
return MAX_ANALOG_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 Analog_Input_Index_To_Instance(unsigned index)
{
return index;
}
char *Analog_Input_Name(uint32_t object_instance)
{
static char text_string[32] = ""; /* okay for single thread */
if (object_instance < MAX_ANALOG_INPUTS) {
sprintf(text_string, "ANALOG INPUT %u", object_instance);
return text_string;
}
return NULL;
}
/* return apdu length, or -1 on error */
/* assumption - object has already exists */
int Analog_Input_Encode_Property_APDU(uint8_t * apdu,
uint32_t object_instance,
BACNET_PROPERTY_ID property,
int32_t array_index,
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code)
{
int apdu_len = 0; /* return value */
BACNET_BIT_STRING bit_string;
BACNET_CHARACTER_STRING char_string;
float value = (float) 3.14;
(void) array_index;
switch (property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_ANALOG_INPUT,
object_instance);
break;
case PROP_OBJECT_NAME:
case PROP_DESCRIPTION:
characterstring_init_ansi(&char_string,
Analog_Input_Name(object_instance));
apdu_len = encode_tagged_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_ANALOG_INPUT);
break;
case PROP_PRESENT_VALUE:
apdu_len = encode_tagged_real(&apdu[0], value);
break;
case PROP_STATUS_FLAGS:
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false);
bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false);
apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string);
break;
case PROP_EVENT_STATE:
apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL);
break;
case PROP_OUT_OF_SERVICE:
apdu_len = encode_tagged_boolean(&apdu[0], false);
break;
case PROP_UNITS:
apdu_len = encode_tagged_enumerated(&apdu[0], UNITS_PERCENT);
break;
case 9997:
apdu_len = encode_tagged_real(&apdu[0], (float) 90.510);
break;
case 9998:
apdu_len = encode_tagged_unsigned(&apdu[0], 90);
break;
/* test case for signed encoding-decoding negative value correctly */
case 9999:
apdu_len = encode_tagged_signed(&apdu[0], -200);
break;
default:
*error_class = ERROR_CLASS_PROPERTY;
*error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = -1;
break;
}
return apdu_len;
}
#ifdef TEST
#include <assert.h>
#include <string.h>
#include "ctest.h"
void testAnalogInput(Test * pTest)
{
uint8_t apdu[MAX_APDU] = { 0 };
int len = 0;
uint32_t len_value = 0;
uint8_t tag_number = 0;
BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_OUTPUT;
uint32_t decoded_instance = 0;
uint32_t instance = 123;
BACNET_ERROR_CLASS error_class;
BACNET_ERROR_CODE error_code;
/* FIXME: we should do a lot more testing here... */
len = Analog_Input_Encode_Property_APDU(&apdu[0],
instance,
PROP_OBJECT_IDENTIFIER,
BACNET_ARRAY_ALL, &error_class, &error_code);
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],
(int *) &decoded_type, &decoded_instance);
ct_test(pTest, decoded_type == OBJECT_ANALOG_INPUT);
ct_test(pTest, decoded_instance == instance);
return;
}
#ifdef TEST_ANALOG_INPUT
int main(void)
{
Test *pTest;
bool rc;
pTest = ct_create("BACnet Analog Input", NULL);
/* individual tests */
rc = ct_addTestFunction(pTest, testAnalogInput);
assert(rc);
ct_setStream(pTest, stdout);
ct_run(pTest);
(void) ct_report(pTest);
ct_destroy(pTest);
return 0;
}
#endif /* TEST_ANALOG_INPUT */
#endif /* TEST */
+55
View File
@@ -0,0 +1,55 @@
/**************************************************************************
*
* 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 AI_H
#define AI_H
#include <stdbool.h>
#include <stdint.h>
#include "bacdef.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
bool Analog_Input_Valid_Instance(uint32_t object_instance);
unsigned Analog_Input_Count(void);
uint32_t Analog_Input_Index_To_Instance(unsigned index);
char *Analog_Input_Name(uint32_t object_instance);
int Analog_Input_Encode_Property_APDU(uint8_t * apdu,
uint32_t object_instance,
BACNET_PROPERTY_ID property,
int32_t array_index,
BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code);
#ifdef TEST
#include "ctest.h"
void testAnalogInput(Test * pTest);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

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