diff --git a/bacnet-stack-0-3-0/.gdbinit b/bacnet-stack-0-3-0/.gdbinit new file mode 100644 index 00000000..a26f7aef --- /dev/null +++ b/bacnet-stack-0-3-0/.gdbinit @@ -0,0 +1,4 @@ +set print pretty on +set print union on +set print address on +list diff --git a/bacnet-stack-0-3-0/.indent.pro b/bacnet-stack-0-3-0/.indent.pro new file mode 100644 index 00000000..730092bc --- /dev/null +++ b/bacnet-stack-0-3-0/.indent.pro @@ -0,0 +1 @@ +-kr -nut -nlp diff --git a/bacnet-stack-0-3-0/Makefile b/bacnet-stack-0-3-0/Makefile new file mode 100644 index 00000000..e68f41fa --- /dev/null +++ b/bacnet-stack-0-3-0/Makefile @@ -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 ) diff --git a/bacnet-stack-0-3-0/abort.c b/bacnet-stack-0-3-0/abort.c new file mode 100644 index 00000000..5d78ff3d --- /dev/null +++ b/bacnet-stack-0-3-0/abort.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/abort.h b/bacnet-stack-0-3-0/abort.h new file mode 100644 index 00000000..6e3fd160 --- /dev/null +++ b/bacnet-stack-0-3-0/abort.h @@ -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 +#include + +#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 diff --git a/bacnet-stack-0-3-0/abort.mak b/bacnet-stack-0-3-0/abort.mak new file mode 100644 index 00000000..753540ec --- /dev/null +++ b/bacnet-stack-0-3-0/abort.mak @@ -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 diff --git a/bacnet-stack-0-3-0/address.c b/bacnet-stack-0-3-0/address.c new file mode 100644 index 00000000..e6755105 --- /dev/null +++ b/bacnet-stack-0-3-0/address.c @@ -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 +#include +#include +#include +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/address.h b/bacnet-stack-0-3-0/address.h new file mode 100644 index 00000000..7c5fd3e4 --- /dev/null +++ b/bacnet-stack-0-3-0/address.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/address.mak b/bacnet-stack-0-3-0/address.mak new file mode 100644 index 00000000..8d11a224 --- /dev/null +++ b/bacnet-stack-0-3-0/address.mak @@ -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 diff --git a/bacnet-stack-0-3-0/apdu.c b/bacnet-stack-0-3-0/apdu.c new file mode 100644 index 00000000..1c8b2968 --- /dev/null +++ b/bacnet-stack-0-3-0/apdu.c @@ -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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/apdu.h b/bacnet-stack-0-3-0/apdu.h new file mode 100644 index 00000000..6bf48bde --- /dev/null +++ b/bacnet-stack-0-3-0/apdu.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/arcnet.h b/bacnet-stack-0-3-0/arcnet.h new file mode 100644 index 00000000..324a0b77 --- /dev/null +++ b/bacnet-stack-0-3-0/arcnet.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/arf.c b/bacnet-stack-0-3-0/arf.c new file mode 100644 index 00000000..7e3bcf33 --- /dev/null +++ b/bacnet-stack-0-3-0/arf.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/arf.h b/bacnet-stack-0-3-0/arf.h new file mode 100644 index 00000000..b693e737 --- /dev/null +++ b/bacnet-stack-0-3-0/arf.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/arf.mak b/bacnet-stack-0-3-0/arf.mak new file mode 100644 index 00000000..d691a917 --- /dev/null +++ b/bacnet-stack-0-3-0/arf.mak @@ -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 diff --git a/bacnet-stack-0-3-0/awf.c b/bacnet-stack-0-3-0/awf.c new file mode 100644 index 00000000..2422210b --- /dev/null +++ b/bacnet-stack-0-3-0/awf.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/awf.h b/bacnet-stack-0-3-0/awf.h new file mode 100644 index 00000000..639a8c98 --- /dev/null +++ b/bacnet-stack-0-3-0/awf.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/awf.mak b/bacnet-stack-0-3-0/awf.mak new file mode 100644 index 00000000..f647b583 --- /dev/null +++ b/bacnet-stack-0-3-0/awf.mak @@ -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 diff --git a/bacnet-stack-0-3-0/bacapp.c b/bacnet-stack-0-3-0/bacapp.c new file mode 100644 index 00000000..bdd98aeb --- /dev/null +++ b/bacnet-stack-0-3-0/bacapp.c @@ -0,0 +1,1195 @@ +/*####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 +#include +#include +#include +#include +#include +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "bactext.h" +#include "datetime.h" + +int bacapp_encode_application_data(uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + apdu[0] = value->tag; + apdu_len++; + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = encode_tagged_boolean(&apdu[0], + value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = encode_tagged_unsigned(&apdu[0], + value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = encode_tagged_signed(&apdu[0], + value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + apdu_len = encode_tagged_real(&apdu[0], value->type.Real); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = encode_tagged_enumerated(&apdu[0], + value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + apdu_len = encode_tagged_date(&apdu[0], &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + apdu_len = encode_tagged_time(&apdu[0], &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = encode_tagged_object_id(&apdu[0], + value->type.Object_Id.type, + value->type.Object_Id.instance); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = encode_tagged_octet_string(&apdu[0], + &value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = encode_tagged_character_string(&apdu[0], + &value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = encode_tagged_bitstring(&apdu[0], + &value->type.Bit_String); + break; + case BACNET_APPLICATION_TAG_DOUBLE: + /* FIXME: double is not implemented yet. + apdu_len = encode_tagged_double(&apdu[0], + value->type.Double); + */ + default: + break; + } + } + + return apdu_len; +} + +/* decode the data and store it into value. + Return the number of octets consumed. */ +int bacapp_decode_data(uint8_t * apdu, + uint8_t tag_data_type, + uint32_t len_value_type, BACNET_APPLICATION_DATA_VALUE * value) +{ + int len = 0; + int object_type = 0; + uint32_t instance = 0; + + if (apdu && value) { + switch (tag_data_type) { + case BACNET_APPLICATION_TAG_NULL: + /* nothing else to do */ + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + value->type.Boolean = decode_boolean(len_value_type); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + len = decode_unsigned(&apdu[0], + len_value_type, &value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + len = decode_signed(&apdu[0], + len_value_type, &value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + len = decode_real(&apdu[0], &(value->type.Real)); + break; +#if 0 + case BACNET_APPLICATION_TAG_DOUBLE: + len = decode_double(&apdu[0], &(value->type.Double)); + break; +#endif + case BACNET_APPLICATION_TAG_ENUMERATED: + len = decode_enumerated(&apdu[0], + len_value_type, &value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + len = decode_date(&apdu[0], &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + len = decode_bacnet_time(&apdu[0], &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + len = decode_object_id(&apdu[0], &object_type, &instance); + value->type.Object_Id.type = object_type; + value->type.Object_Id.instance = instance; + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = decode_octet_string(&apdu[0], + len_value_type, &value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = decode_character_string(&apdu[0], + len_value_type, &value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + len = decode_bitstring(&apdu[0], + len_value_type, &value->type.Bit_String); + break; + default: + break; + } + } + + return len; +} + +int bacapp_decode_application_data(uint8_t * apdu, + int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value) +{ + int len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* FIXME: use max_apdu_len! */ + (void) max_apdu_len; + if (apdu && value && !decode_is_context_specific(apdu)) { + value->context_specific = false; + tag_len = decode_tag_number_and_value(&apdu[0], + &tag_number, &len_value_type); + if (tag_len) { + len += tag_len; + value->tag = tag_number; + len += bacapp_decode_data(&apdu[len], + tag_number, len_value_type, value); + } + value->next = NULL; + } + + return len; +} + +int bacapp_encode_context_data_value(uint8_t * apdu, + uint8_t context_tag_number, BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + apdu_len = encode_context_null(&apdu[0], context_tag_number); + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = encode_context_boolean(&apdu[0], context_tag_number, + value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_context_unsigned(&apdu[0], context_tag_number, + value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = encode_context_signed(&apdu[0], context_tag_number, + value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + apdu_len = encode_context_real(&apdu[0], context_tag_number, + value->type.Real); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_context_enumerated(&apdu[0], context_tag_number, + value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + apdu_len = encode_context_date(&apdu[0], context_tag_number, + &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + apdu_len = encode_context_time(&apdu[0], context_tag_number, + &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = + encode_context_object_id(&apdu[0], context_tag_number, + value->type.Object_Id.type, + value->type.Object_Id.instance); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_context_octet_string(&apdu[0], context_tag_number, + &value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = + encode_context_character_string(&apdu[0], + context_tag_number, &value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = + encode_context_bitstring(&apdu[0], context_tag_number, + &value->type.Bit_String); + break; +#if 0 + case BACNET_APPLICATION_TAG_DOUBLE: + /* FIXME: double is not implemented yet. */ + apdu_len = encode_context_double(&apdu[0], context_tag_number, + value->type.Double); + break; +#endif + default: + break; + } + } + + return apdu_len; +} + +/* returns the fixed tag type for certain context tagged properties */ +BACNET_APPLICATION_TAG bacapp_context_tag_type(BACNET_PROPERTY_ID property, + uint8_t tag_number) +{ + BACNET_APPLICATION_TAG tag = MAX_BACNET_APPLICATION_TAG; + + switch (property) { + case PROP_REQUESTED_SHED_LEVEL: + switch (tag_number) { + case 0: + case 1: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 2: + tag = BACNET_APPLICATION_TAG_REAL; + break; + default: + break; + } + break; + case PROP_ACTION: + switch (tag_number) { + case 0: + case 1: + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + case 2: + tag = BACNET_APPLICATION_TAG_ENUMERATED; + break; + case 3: + case 5: + case 6: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 7: + case 8: + tag = BACNET_APPLICATION_TAG_BOOLEAN; + break; + case 4: /* propertyValue: abstract syntax */ + default: + break; + } + break; + case PROP_EXCEPTION_SCHEDULE: + switch (tag_number) { + case 1: + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + case 3: + tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + break; + case 0: /* calendarEntry: abstract syntax + context */ + case 2: /* list of BACnetTimeValue: abstract syntax */ + default: + break; + } + break; + default: + break; + } + + return tag; +} + +int bacapp_encode_context_data(uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value, BACNET_PROPERTY_ID property) +{ + int apdu_len = 0; + BACNET_APPLICATION_TAG tag_data_type; + + if (value && apdu) { + tag_data_type = + bacapp_context_tag_type(property, value->context_tag); + if (tag_data_type < MAX_BACNET_APPLICATION_TAG) { + apdu_len = bacapp_encode_context_data_value(&apdu[0], + value->context_tag, value); + } else { + /* FIXME: what now? */ + apdu_len = 0; + } + value->next = NULL; + } + + return apdu_len; +} + +int bacapp_decode_context_data(uint8_t * apdu, + int max_apdu_len, BACNET_APPLICATION_DATA_VALUE * value, + BACNET_PROPERTY_ID property) +{ + int apdu_len = 0, len = 0; + int tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + + /* FIXME: use max_apdu_len! */ + (void) max_apdu_len; + if (apdu && value && decode_is_context_specific(apdu)) { + value->context_specific = true; + tag_len = decode_tag_number_and_value(&apdu[0], + &tag_number, &len_value_type); + if (tag_len) { + apdu_len = tag_len; + value->context_tag = tag_number; + value->tag = bacapp_context_tag_type(property, tag_number); + if (value->tag < MAX_BACNET_APPLICATION_TAG) { + len = bacapp_decode_data(&apdu[apdu_len], + value->tag, len_value_type, value); + apdu_len += len; + } else { + /* FIXME: what now? */ + apdu_len = 0; + } + } + value->next = NULL; + } + + return apdu_len; +} + +int bacapp_encode_data(uint8_t * apdu, + BACNET_APPLICATION_DATA_VALUE * value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (value && apdu) { + if (value->context_specific) { + apdu_len = bacapp_encode_context_data_value(&apdu[0], + value->context_tag, value); + } else { + apdu_len = bacapp_encode_application_data(&apdu[0], value); + } + } + + return apdu_len; +} + + +bool bacapp_copy(BACNET_APPLICATION_DATA_VALUE * dest_value, + BACNET_APPLICATION_DATA_VALUE * src_value) +{ + bool status = true; /*return value */ + + if (dest_value && src_value) { + dest_value->tag = src_value->tag; + switch (src_value->tag) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + dest_value->type.Boolean = src_value->type.Boolean; + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + dest_value->type.Unsigned_Int = src_value->type.Unsigned_Int; + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + dest_value->type.Signed_Int = src_value->type.Signed_Int; + break; + case BACNET_APPLICATION_TAG_REAL: + dest_value->type.Real = src_value->type.Real; + break; + case BACNET_APPLICATION_TAG_DOUBLE: + dest_value->type.Double = src_value->type.Double; + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + dest_value->type.Enumerated = src_value->type.Enumerated; + break; + case BACNET_APPLICATION_TAG_DATE: + datetime_copy_date(&dest_value->type.Date, + &src_value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + datetime_copy_time(&dest_value->type.Time, + &src_value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + dest_value->type.Object_Id.type = + src_value->type.Object_Id.type; + dest_value->type.Object_Id.instance = + src_value->type.Object_Id.instance; + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + octetstring_copy(&dest_value->type.Octet_String, + &src_value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + characterstring_copy(&dest_value->type.Character_String, + &src_value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + default: + status = false; + break; + } + dest_value->next = src_value->next; + } + + return status; +} + +/* 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) +{ + int len = 0; + int total_len = 0; + int apdu_len = 0; + uint8_t tag_number = 0; + uint8_t opening_tag_number = 0; + uint8_t opening_tag_number_counter = 0; + uint32_t value = 0; + BACNET_APPLICATION_DATA_VALUE application_value; + + if (decode_is_opening_tag(&apdu[0])) { + len = decode_tag_number_and_value(&apdu[apdu_len], + &tag_number, &value); + apdu_len += len; + opening_tag_number = tag_number; + opening_tag_number_counter = 1; + while (opening_tag_number_counter) { + if (decode_is_opening_tag(&apdu[apdu_len])) { + len = decode_tag_number_and_value(&apdu[apdu_len], + &tag_number, &value); + if (tag_number == opening_tag_number) + opening_tag_number_counter++; + } else if (decode_is_closing_tag(&apdu[apdu_len])) { + len = decode_tag_number_and_value(&apdu[apdu_len], + &tag_number, &value); + if (tag_number == opening_tag_number) + opening_tag_number_counter--; + } else if (decode_is_context_specific(&apdu[apdu_len])) { + /* context-specific tagged data */ + len = bacapp_decode_context_data(&apdu[apdu_len], + max_apdu_len - apdu_len, &application_value, property); + } else { + /* application tagged data */ + len = bacapp_decode_application_data(&apdu[apdu_len], + max_apdu_len - apdu_len, &application_value); + } + apdu_len += len; + if (opening_tag_number_counter) + total_len += len; + /* ERROR! */ + if (apdu_len > max_apdu_len) { + total_len = -1; + break; + } + } + } + + return total_len; +} + +#ifdef BACAPP_PRINT_ENABLED +bool bacapp_print_value(FILE * stream, + BACNET_APPLICATION_DATA_VALUE * value, BACNET_PROPERTY_ID property) +{ + bool status = true; /*return value */ + size_t len = 0, i = 0; + char *char_str; + uint8_t *octet_str; + + if (value) { + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + fprintf(stream, "Null"); + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + fprintf(stream, "%s", value->type.Boolean ? "TRUE" : "FALSE"); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + fprintf(stream, "%u", value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + fprintf(stream, "%d", value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + fprintf(stream, "%f", (double) value->type.Real); + break; + case BACNET_APPLICATION_TAG_DOUBLE: + fprintf(stream, "%f", value->type.Double); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + switch (property) { + case PROP_OBJECT_TYPE: + fprintf(stream, "%s", + bactext_object_type_name(value->type.Enumerated)); + break; + case PROP_EVENT_STATE: + fprintf(stream, "%s", + bactext_event_state_name(value->type.Enumerated)); + break; + case PROP_UNITS: + fprintf(stream, "%s", + bactext_engineering_unit_name(value->type.Enumerated)); + break; + case PROP_PRESENT_VALUE: + fprintf(stream, "%s", + bactext_binary_present_value_name(value->type. + Enumerated)); + break; + case PROP_RELIABILITY: + fprintf(stream, "%s", + bactext_reliability_name(value->type.Enumerated)); + break; + case PROP_SYSTEM_STATUS: + fprintf(stream, "%s", + bactext_device_status_name(value->type.Enumerated)); + break; + case PROP_SEGMENTATION_SUPPORTED: + fprintf(stream, "%s", + bactext_segmentation_name(value->type.Enumerated)); + break; + default: + fprintf(stream, "%u", value->type.Enumerated); + break; + } + break; + case BACNET_APPLICATION_TAG_DATE: + fprintf(stream, "%s, %s %u, %u", + bactext_day_of_week_name(value->type.Date.wday), + bactext_month_name(value->type.Date.month), + (unsigned) value->type.Date.day, + (unsigned) value->type.Date.year); + break; + case BACNET_APPLICATION_TAG_TIME: + fprintf(stream, "%02u:%02u:%02u.%03u", + (unsigned) value->type.Time.hour, + (unsigned) value->type.Time.min, + (unsigned) value->type.Time.sec, + (unsigned) value->type.Time.hundredths); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + fprintf(stream, "%s %u", + bactext_object_type_name(value->type.Object_Id.type), + value->type.Object_Id.instance); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = octetstring_length(&value->type.Octet_String); + octet_str = octetstring_value(&value->type.Octet_String); + for (i = 0; i < len; i++) { + fprintf(stream, "%02X", *octet_str); + octet_str++; + } + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = characterstring_length(&value->type.Character_String); + char_str = + characterstring_value(&value->type.Character_String); + fprintf(stream, "\""); + for (i = 0; i < len; i++) { + if (isprint(*char_str)) + fprintf(stream, "%c", *char_str); + else + fprintf(stream, "."); + char_str++; + } + fprintf(stream, "\""); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + len = bitstring_bits_used(&value->type.Bit_String); + fprintf(stream, "{"); + for (i = 0; i < len; i++) { + fprintf(stream, "%s", + bitstring_bit(&value->type.Bit_String, + (uint8_t) i) ? "true" : "false"); + if (i < len - 1) + fprintf(stream, ","); + } + fprintf(stream, "}"); + break; + default: + status = false; + break; + } + } + + return status; +} + +/* used to load the app data struct with the proper data + converted from a command line argument */ +bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number, + const char *argv, BACNET_APPLICATION_DATA_VALUE * value) +{ + int hour, min, sec, hundredths; + int year, month, day, wday; + int object_type = 0; + uint32_t instance = 0; + bool status = false; + long long_value = 0; + unsigned long unsigned_long_value = 0; + double double_value = 0.0; + int count = 0; + + if (value && (tag_number < MAX_BACNET_APPLICATION_TAG)) { + status = true; + value->tag = tag_number; + if (tag_number == BACNET_APPLICATION_TAG_BOOLEAN) { + long_value = strtol(argv, NULL, 0); + if (long_value) + value->type.Boolean = true; + else + value->type.Boolean = false; + } else if (tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + unsigned_long_value = strtoul(argv, NULL, 0); + value->type.Unsigned_Int = unsigned_long_value; + } else if (tag_number == BACNET_APPLICATION_TAG_SIGNED_INT) { + long_value = strtol(argv, NULL, 0); + value->type.Signed_Int = long_value; + } else if (tag_number == BACNET_APPLICATION_TAG_REAL) { + double_value = strtod(argv, NULL); + value->type.Real = (float) double_value; + } else if (tag_number == BACNET_APPLICATION_TAG_DOUBLE) { + double_value = strtod(argv, NULL); + value->type.Double = double_value; + } else if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + status = + characterstring_init_ansi(&value->type.Character_String, + (char *) argv); + } else if (tag_number == BACNET_APPLICATION_TAG_OCTET_STRING) { + status = octetstring_init(&value->type.Octet_String, + (uint8_t *) argv, strlen(argv)); + } else if (tag_number == BACNET_APPLICATION_TAG_ENUMERATED) { + unsigned_long_value = strtoul(argv, NULL, 0); + value->type.Enumerated = unsigned_long_value; + } else if (tag_number == BACNET_APPLICATION_TAG_DATE) { + count = + sscanf(argv, "%d/%d/%d:%d", &year, &month, &day, &wday); + if (count == 3) { + datetime_set_date(&value->type.Date, + (uint16_t)year, (uint8_t)month, (uint8_t)day); + } else if (count == 4) { + value->type.Date.year = year; + value->type.Date.month = month; + value->type.Date.day = day; + value->type.Date.wday = wday; + } else + status = false; + } else if (tag_number == BACNET_APPLICATION_TAG_TIME) { + count = + sscanf(argv, "%d:%d:%d.%d", &hour, &min, &sec, + &hundredths); + if (count == 4) { + value->type.Time.hour = hour; + value->type.Time.min = min; + value->type.Time.sec = sec; + value->type.Time.hundredths = hundredths; + } else if (count == 3) { + value->type.Time.hour = hour; + value->type.Time.min = min; + value->type.Time.sec = sec; + value->type.Time.hundredths = 0; + } else if (count == 2) { + value->type.Time.hour = hour; + value->type.Time.min = min; + value->type.Time.sec = 0; + value->type.Time.hundredths = 0; + } else + status = false; + } else if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { + count = sscanf(argv, "%d:%d", &object_type, &instance); + if (count == 2) { + value->type.Object_Id.type = object_type; + value->type.Object_Id.instance = instance; + } else + status = false; + } + value->next = NULL; + } + + return status; +} +#endif + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBACnetApplicationDataLength(Test * pTest) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int len = 0; /* total length of the apdu, return value */ + int test_len = 0; /* length of the data */ + uint8_t apdu[480] = { 0 }; + BACNET_TIME local_time; + BACNET_DATE local_date; + + /* create some constructed data */ + /* 1. zero elements */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES); + ct_test(pTest, test_len == len); + + /* 2. application tagged data, one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], 4194303); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_OBJECT_IDENTIFIER); + ct_test(pTest, test_len == len); + + /* 3. application tagged data, multiple elements */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], 1); + test_len += len; + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], 42); + test_len += len; + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], 91); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_tagged_null(&apdu[apdu_len]); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_PRIORITY_ARRAY); + ct_test(pTest, test_len == len); + + /* 4. complex datatype - one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_opening_tag(&apdu[apdu_len], 3); + test_len += len; + apdu_len += len; + local_date.year = 2006; /* AD */ + local_date.month = 4; /* 1=Jan */ + local_date.day = 1; /* 1..31 */ + local_date.wday = 6; /* 1=Monday */ + len = encode_tagged_date(&apdu[apdu_len], &local_date); + test_len += len; + apdu_len += len; + local_time.hour = 7; + local_time.min = 0; + local_time.sec = 3; + local_time.hundredths = 1; + len = encode_tagged_time(&apdu[apdu_len], &local_time); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_START_TIME); + ct_test(pTest, test_len == len); + + /* 5. complex datatype - multiple elements */ + + + + /* 6. context tagged data, one element */ + test_len = 0; + apdu_len = 0; + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + len = encode_context_unsigned(&apdu[apdu_len], 1, 91); + test_len += len; + apdu_len += len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* verify the length of the data inside the opening/closing tags */ + len = bacapp_data_len(&apdu[0], apdu_len, PROP_REQUESTED_SHED_LEVEL); + ct_test(pTest, test_len == len); +} + +/* generic - can be used by other unit tests + returns true if matching or same, false if different */ +bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE * value, + BACNET_APPLICATION_DATA_VALUE * test_value) +{ + bool status = true; /*return value */ + + /* does the tag match? */ + if (test_value->tag != value->tag) + status = false; + if (status) { + /* does the value match? */ + switch (test_value->tag) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + if (test_value->type.Boolean != value->type.Boolean) + status = false; + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (test_value->type.Unsigned_Int != value->type.Unsigned_Int) + status = false; + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (test_value->type.Signed_Int != value->type.Signed_Int) + status = false; + break; + case BACNET_APPLICATION_TAG_REAL: + if (test_value->type.Real != value->type.Real) + status = false; + break; + case BACNET_APPLICATION_TAG_DOUBLE: + if (test_value->type.Double != value->type.Double) + status = false; + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + if (test_value->type.Enumerated != value->type.Enumerated) + status = false; + break; + case BACNET_APPLICATION_TAG_DATE: + if (datetime_compare_date(&test_value->type.Date, + &value->type.Date) != 0) + status = false; + break; + case BACNET_APPLICATION_TAG_TIME: + if (datetime_compare_time(&test_value->type.Time, + &value->type.Time) != 0) + status = false; + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + if (test_value->type.Object_Id.type != + value->type.Object_Id.type) + status = false; + if (test_value->type.Object_Id.instance != + value->type.Object_Id.instance) + status = false; + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + status = characterstring_same(&value->type.Character_String, + &test_value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + default: + status = false; + break; + } + } + return status; +} + +static bool testBACnetApplicationDataValue(BACNET_APPLICATION_DATA_VALUE * + value) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_APPLICATION_DATA_VALUE test_value; + + apdu_len = bacapp_encode_application_data(&apdu[0], value); + len = bacapp_decode_application_data(&apdu[0], apdu_len, &test_value); + + return bacapp_same_value(value, &test_value); +} + +void testBACnetApplicationData(Test * pTest) +{ + BACNET_APPLICATION_DATA_VALUE value; + bool status = false; + + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_NULL, + NULL, &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_BOOLEAN, + "1", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Boolean == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_BOOLEAN, + "0", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Boolean == false); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, + "0", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, + "0xFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0xFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_UNSIGNED_INT, + "0xFFFFFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Unsigned_Int == 0xFFFFFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "0", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "-1", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == -1); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "32768", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == 32768); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_SIGNED_INT, + "-32768", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Signed_Int == -32768); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, + "0.0", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, + "-1.0", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, + "1.0", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, + "3.14159", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_REAL, + "-3.14159", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, + "0", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, + "0xFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0xFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_ENUMERATED, + "0xFFFFFFFF", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Enumerated == 0xFFFFFFFF); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, + "2005/5/22:1", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2005); + ct_test(pTest, value.type.Date.month == 5); + ct_test(pTest, value.type.Date.day == 22); + ct_test(pTest, value.type.Date.wday == 1); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + /* Happy Valentines Day! */ + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, + "2007/2/14", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2007); + ct_test(pTest, value.type.Date.month == 2); + ct_test(pTest, value.type.Date.day == 14); + ct_test(pTest, value.type.Date.wday == BACNET_WEEKDAY_WEDNESDAY); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + /* Wildcard Values */ + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_DATE, + "2155/255/255:255", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Date.year == 2155); + ct_test(pTest, value.type.Date.month == 255); + ct_test(pTest, value.type.Date.day == 255); + ct_test(pTest, value.type.Date.wday == 255); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, + "23:59:59.12", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 59); + ct_test(pTest, value.type.Time.hundredths == 12); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, + "23:59:59", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 59); + ct_test(pTest, value.type.Time.hundredths == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = bacapp_parse_application_data(BACNET_APPLICATION_TAG_TIME, + "23:59", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Time.hour == 23); + ct_test(pTest, value.type.Time.min == 59); + ct_test(pTest, value.type.Time.sec == 0); + ct_test(pTest, value.type.Time.hundredths == 0); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data(BACNET_APPLICATION_TAG_OBJECT_ID, + "0:100", &value); + ct_test(pTest, status == true); + ct_test(pTest, value.type.Object_Id.type == 0); + ct_test(pTest, value.type.Object_Id.instance == 100); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + status = + bacapp_parse_application_data + (BACNET_APPLICATION_TAG_CHARACTER_STRING, "Karg!", &value); + ct_test(pTest, status == true); + ct_test(pTest, testBACnetApplicationDataValue(&value)); + + return; +} + +#ifdef TEST_BACNET_APPLICATION_DATA +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Application Data", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACnetApplicationData); + assert(rc); + rc = ct_addTestFunction(pTest, testBACnetApplicationDataLength); + assert(rc); + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BACNET_APPLICATION_DATA */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/bacapp.h b/bacnet-stack-0-3-0/bacapp.h new file mode 100644 index 00000000..c63410ea --- /dev/null +++ b/bacnet-stack-0-3-0/bacapp.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/bacapp.ide b/bacnet-stack-0-3-0/bacapp.ide new file mode 100644 index 00000000..9beee0ee Binary files /dev/null and b/bacnet-stack-0-3-0/bacapp.ide differ diff --git a/bacnet-stack-0-3-0/bacapp.mak b/bacnet-stack-0-3-0/bacapp.mak new file mode 100644 index 00000000..458c2e90 --- /dev/null +++ b/bacnet-stack-0-3-0/bacapp.mak @@ -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 diff --git a/bacnet-stack-0-3-0/bacdcode.c b/bacnet-stack-0-3-0/bacdcode.c new file mode 100644 index 00000000..5e66e122 --- /dev/null +++ b/bacnet-stack-0-3-0/bacdcode.c @@ -0,0 +1,2131 @@ +/*####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 + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bits.h" +#include "bacstr.h" + +/* NOTE: byte order plays a role in decoding multibyte values */ +/* http://www.unixpapa.com/incnote/byteorder.html */ +/* define BIG_ENDIAN=1 or BIG_ENDIAN=0 for your target! */ + +/* max-segments-accepted + B'000' Unspecified number of segments accepted. + B'001' 2 segments accepted. + B'010' 4 segments accepted. + B'011' 8 segments accepted. + B'100' 16 segments accepted. + B'101' 32 segments accepted. + B'110' 64 segments accepted. + B'111' Greater than 64 segments accepted. +*/ + +/* max-APDU-length-accepted + B'0000' Up to MinimumMessageSize (50 octets) + B'0001' Up to 128 octets + B'0010' Up to 206 octets (fits in a LonTalk frame) + B'0011' Up to 480 octets (fits in an ARCNET frame) + B'0100' Up to 1024 octets + B'0101' Up to 1476 octets (fits in an ISO 8802-3 frame) + B'0110' reserved by ASHRAE + B'0111' reserved by ASHRAE + B'1000' reserved by ASHRAE + B'1001' reserved by ASHRAE + B'1010' reserved by ASHRAE + B'1011' reserved by ASHRAE + B'1100' reserved by ASHRAE + B'1101' reserved by ASHRAE + B'1110' reserved by ASHRAE + B'1111' reserved by ASHRAE +*/ +/* 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) +{ + uint8_t octet = 0; + + if (max_segs < 2) + octet = 0; + else if (max_segs < 4) + octet = 0x10; + else if (max_segs < 8) + octet = 0x20; + else if (max_segs < 16) + octet = 0x30; + else if (max_segs < 32) + octet = 0x40; + else if (max_segs < 64) + octet = 0x50; + else if (max_segs == 64) + octet = 0x60; + else + octet = 0x70; + + /* max_apdu must be 50 octets minimum */ + if (max_apdu <= 50) + octet |= 0x00; + else if (max_apdu <= 128) + octet |= 0x01; + /*fits in a LonTalk frame */ + else if (max_apdu <= 206) + octet |= 0x02; + /*fits in an ARCNET or MS/TP frame */ + else if (max_apdu <= 480) + octet |= 0x03; + else if (max_apdu <= 1024) + octet |= 0x04; + /* fits in an ISO 8802-3 frame */ + else if (max_apdu <= 1476) + octet |= 0x05; + + return octet; +} + +/* from clause 20.1.2.4 max-segments-accepted */ +/* and clause 20.1.2.5 max-APDU-length-accepted */ +/* returns the encoded octet */ +int decode_max_segs(uint8_t octet) +{ + int max_segs = 0; + + switch (octet & 0xF0) { + case 0: + max_segs = 0; + break; + case 0x10: + max_segs = 2; + break; + case 0x20: + max_segs = 4; + break; + case 0x30: + max_segs = 8; + break; + case 0x40: + max_segs = 16; + break; + case 0x50: + max_segs = 32; + break; + case 0x60: + max_segs = 64; + break; + case 0x70: + max_segs = 65; + break; + default: + break; + } + + return max_segs; +} + +int decode_max_apdu(uint8_t octet) +{ + int max_apdu = 0; + + switch (octet & 0x0F) { + case 0: + max_apdu = 50; + break; + case 1: + max_apdu = 128; + break; + case 2: + max_apdu = 206; + break; + case 3: + max_apdu = 480; + break; + case 4: + max_apdu = 1024; + break; + case 5: + max_apdu = 1476; + break; + default: + break; + } + + return max_apdu; +} + +int encode_unsigned16(uint8_t * apdu, uint16_t value) +{ + union { + uint8_t byte[2]; + uint16_t value; + } short_data = { { + 0}}; + + short_data.value = value; +#if BIG_ENDIAN + apdu[0] = short_data.byte[0]; + apdu[1] = short_data.byte[1]; +#else + apdu[0] = short_data.byte[1]; + apdu[1] = short_data.byte[0]; +#endif + + return 2; +} + +int decode_unsigned16(uint8_t * apdu, uint16_t * value) +{ + union { + uint8_t byte[2]; + uint16_t value; + } short_data = { { + 0}}; + +#if BIG_ENDIAN + short_data.byte[0] = apdu[0]; + short_data.byte[1] = apdu[1]; +#else + short_data.byte[1] = apdu[0]; + short_data.byte[0] = apdu[1]; +#endif + if (value) + *value = short_data.value; + + return 2; +} + +int encode_unsigned24(uint8_t * apdu, uint32_t value) +{ + union { + uint8_t byte[4]; + uint32_t value; + } long_data = { { + 0}}; + + long_data.value = value; +#if BIG_ENDIAN + apdu[0] = long_data.byte[1]; + apdu[1] = long_data.byte[2]; + apdu[2] = long_data.byte[3]; +#else + apdu[0] = long_data.byte[2]; + apdu[1] = long_data.byte[1]; + apdu[2] = long_data.byte[0]; +#endif + + return 3; +} + +int decode_unsigned24(uint8_t * apdu, uint32_t * value) +{ + union { + uint8_t byte[4]; + uint32_t value; + } long_data = { { + 0}}; + +#if BIG_ENDIAN + long_data.byte[1] = apdu[0]; + long_data.byte[2] = apdu[1]; + long_data.byte[3] = apdu[2]; +#else + long_data.byte[2] = apdu[0]; + long_data.byte[1] = apdu[1]; + long_data.byte[0] = apdu[2]; +#endif + if (value) + *value = long_data.value; + + return 3; +} + +int encode_unsigned32(uint8_t * apdu, uint32_t value) +{ + union { + uint8_t byte[4]; + uint32_t value; + } long_data = { { + 0}}; + + long_data.value = value; +#if BIG_ENDIAN + apdu[0] = long_data.byte[0]; + apdu[1] = long_data.byte[1]; + apdu[2] = long_data.byte[2]; + apdu[3] = long_data.byte[3]; +#else + apdu[0] = long_data.byte[3]; + apdu[1] = long_data.byte[2]; + apdu[2] = long_data.byte[1]; + apdu[3] = long_data.byte[0]; +#endif + + return 4; +} + +int decode_unsigned32(uint8_t * apdu, uint32_t * value) +{ + union { + uint8_t byte[4]; + uint32_t value; + } long_data = { { + 0}}; + +#if BIG_ENDIAN + long_data.byte[0] = apdu[0]; + long_data.byte[1] = apdu[1]; + long_data.byte[2] = apdu[2]; + long_data.byte[3] = apdu[3]; +#else + long_data.byte[3] = apdu[0]; + long_data.byte[2] = apdu[1]; + long_data.byte[1] = apdu[2]; + long_data.byte[0] = apdu[3]; +#endif + if (value) + *value = long_data.value; + + return 4; +} + +int encode_signed8(uint8_t * apdu, int8_t value) +{ + union { + uint8_t byte; + int8_t value; + } byte_data = { + 0}; + + byte_data.value = value; + apdu[0] = byte_data.byte; + + return 1; +} + +int decode_signed8(uint8_t * apdu, int8_t * value) +{ + union { + uint8_t byte; + int8_t value; + } byte_data = { + 0}; + + byte_data.byte = apdu[0]; + if (value) + *value = byte_data.value; + + return 1; +} + +int encode_signed16(uint8_t * apdu, int16_t value) +{ + union { + uint8_t byte[2]; + int16_t value; + } short_data = { { + 0}}; + + short_data.value = value; +#if BIG_ENDIAN + apdu[0] = short_data.byte[0]; + apdu[1] = short_data.byte[1]; +#else + apdu[0] = short_data.byte[1]; + apdu[1] = short_data.byte[0]; +#endif + + return 2; +} + +int decode_signed16(uint8_t * apdu, int16_t * value) +{ + union { + uint8_t byte[2]; + int16_t value; + } short_data = { { + 0}}; + +#if BIG_ENDIAN + short_data.byte[0] = apdu[0]; + short_data.byte[1] = apdu[1]; +#else + short_data.byte[1] = apdu[0]; + short_data.byte[0] = apdu[1]; +#endif + if (value) + *value = short_data.value; + + return 2; +} + +int encode_signed24(uint8_t * apdu, int32_t value) +{ + union { + uint8_t byte[4]; + int32_t value; + } long_data = { { + 0}}; + + long_data.value = value; +#if BIG_ENDIAN + apdu[0] = long_data.byte[1]; + apdu[1] = long_data.byte[2]; + apdu[2] = long_data.byte[3]; +#else + apdu[0] = long_data.byte[2]; + apdu[1] = long_data.byte[1]; + apdu[2] = long_data.byte[0]; +#endif + + return 3; +} + +int decode_signed24(uint8_t * apdu, int32_t * value) +{ + union { + uint8_t byte[4]; + int32_t value; + } long_data = { { + 0}}; + +#if BIG_ENDIAN + /* negative - bit 7 is set */ + if (apdu[0] & 0x80) + long_data.byte[0] = 0xFF; + /* fill in the rest */ + long_data.byte[1] = apdu[0]; + long_data.byte[2] = apdu[1]; + long_data.byte[3] = apdu[2]; +#else + /* negative - bit 7 is set */ + if (apdu[0] & 0x80) + long_data.byte[3] = 0xFF; + /* fill in the rest */ + long_data.byte[2] = apdu[0]; + long_data.byte[1] = apdu[1]; + long_data.byte[0] = apdu[2]; +#endif + if (value) + *value = long_data.value; + + return 3; +} + +int encode_signed32(uint8_t * apdu, int32_t value) +{ + union { + uint8_t byte[4]; + int32_t value; + } long_data = { { + 0}}; + + long_data.value = value; +#if BIG_ENDIAN + apdu[0] = long_data.byte[0]; + apdu[1] = long_data.byte[1]; + apdu[2] = long_data.byte[2]; + apdu[3] = long_data.byte[3]; +#else + apdu[0] = long_data.byte[3]; + apdu[1] = long_data.byte[2]; + apdu[2] = long_data.byte[1]; + apdu[3] = long_data.byte[0]; +#endif + + return 4; +} + +int decode_signed32(uint8_t * apdu, int32_t * value) +{ + union { + uint8_t byte[4]; + int32_t value; + } long_data = { { + 0}}; + +#if BIG_ENDIAN + long_data.byte[0] = apdu[0]; + long_data.byte[1] = apdu[1]; + long_data.byte[2] = apdu[2]; + long_data.byte[3] = apdu[3]; +#else + long_data.byte[3] = apdu[0]; + long_data.byte[2] = apdu[1]; + long_data.byte[1] = apdu[2]; + long_data.byte[0] = apdu[3]; +#endif + if (value) + *value = long_data.value; + + return 4; +} + +/* from clause 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tag(uint8_t * apdu, uint8_t tag_number, bool context_specific, + uint32_t len_value_type) +{ + int len = 1; /* return value */ + + apdu[0] = 0; + if (context_specific) + apdu[0] = BIT3; + + /* additional tag byte after this byte */ + /* for extended tag byte */ + if (tag_number <= 14) + apdu[0] |= (tag_number << 4); + else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + + /* NOTE: additional len byte(s) after extended tag byte */ + /* if larger than 4 */ + if (len_value_type <= 4) + apdu[0] |= len_value_type; + else { + apdu[0] |= 5; + if (len_value_type <= 253) { + apdu[len++] = (uint8_t) len_value_type; + } else if (len_value_type <= 65535) { + apdu[len++] = 254; + len += + encode_unsigned16(&apdu[len], (uint16_t) len_value_type); + } else { + apdu[len++] = 255; + len += encode_unsigned32(&apdu[len], len_value_type); + } + } + + return len; +} + +/* 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 len = 1; + + /* set class field to context specific */ + apdu[0] = BIT3; + /* additional tag byte after this byte for extended tag byte */ + if (tag_number <= 14) + apdu[0] |= (tag_number << 4); + else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + /* set type field to opening tag */ + apdu[0] |= 6; + + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +int encode_closing_tag(uint8_t * apdu, uint8_t tag_number) +{ + int len = 1; + + /* set class field to context specific */ + apdu[0] = BIT3; + /* additional tag byte after this byte for extended tag byte */ + if (tag_number <= 14) + apdu[0] |= (tag_number << 4); + else { + apdu[0] |= 0xF0; + apdu[1] = tag_number; + len++; + } + /* set type field to closing tag */ + apdu[0] |= 7; + + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if extended tag numbering is used */ +static bool decode_is_extended_tag_number(uint8_t * apdu) +{ + return ((apdu[0] & 0xF0) == 0xF0); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the extended value is used */ +static bool decode_is_extended_value(uint8_t * apdu) +{ + return ((apdu[0] & 0x07) == 5); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the tag is context specific */ +bool decode_is_context_specific(uint8_t * apdu) +{ + return ((apdu[0] & BIT3) == BIT3); +} + +int decode_tag_number(uint8_t * apdu, uint8_t * tag_number) +{ + int len = 1; /* return value */ + + /* decode the tag number first */ + if (decode_is_extended_tag_number(&apdu[0])) { + /* extended tag */ + if (tag_number) + *tag_number = apdu[1]; + len++; + } else { + if (tag_number) + *tag_number = (apdu[0] >> 4); + } + + return len; +} + +bool decode_is_opening_tag(uint8_t * apdu) +{ + return ((apdu[0] & 0x07) == 6); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +bool decode_is_closing_tag(uint8_t * apdu) +{ + return ((apdu[0] & 0x07) == 7); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +int decode_tag_number_and_value(uint8_t * apdu, + uint8_t * tag_number, uint32_t * value) +{ + int len = 1; + uint16_t value16; + uint32_t value32; + + len = decode_tag_number(&apdu[0], tag_number); + /* decode the value */ + if (decode_is_extended_value(&apdu[0])) { + /* tagged as uint32_t */ + if (apdu[len] == 255) { + len++; + len += decode_unsigned32(&apdu[len], &value32); + if (value) + *value = value32; + } + /* tagged as uint16_t */ + else if (apdu[len] == 254) { + len++; + len += decode_unsigned16(&apdu[len], &value16); + if (value) + *value = value16; + } + /* no tag - must be uint8_t */ + else { + if (value) + *value = apdu[len]; + len++; + } + } else if (decode_is_opening_tag(&apdu[0]) && value) + *value = 0; + /* closing tag */ + else if (decode_is_closing_tag(&apdu[0]) && value) + *value = 0; + /* small value */ + else if (value) + *value = apdu[0] & 0x07; + + return len; +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns true if the tag is context specific and matches */ +bool decode_is_context_tag(uint8_t * apdu, uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + bool context_specific = false; + + context_specific = decode_is_context_specific(apdu); + decode_tag_number(apdu, &my_tag_number); + + return (context_specific && (my_tag_number == tag_number)); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +bool decode_is_opening_tag_number(uint8_t * apdu, uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + bool opening_tag = false; + + opening_tag = ((apdu[0] & 0x07) == 6); + decode_tag_number(apdu, &my_tag_number); + + return (opening_tag && (my_tag_number == tag_number)); +} + +/* from clause 20.2.1.3.2 Constructed Data */ +/* returns the number of apdu bytes consumed */ +bool decode_is_closing_tag_number(uint8_t * apdu, uint8_t tag_number) +{ + uint8_t my_tag_number = 0; + bool closing_tag = false; + + closing_tag = ((apdu[0] & 0x07) == 7); + decode_tag_number(apdu, &my_tag_number); + + return (closing_tag && (my_tag_number == tag_number)); +} + +/* from clause 20.2.3 Encoding of a Boolean Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tagged_boolean(uint8_t * apdu, bool boolean_value) +{ + int len = 0; + uint32_t len_value = 0; + + if (boolean_value) + len_value = 1; + + len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BOOLEAN, false, + len_value); + + return len; +} + +/* context tagged is encoded differently */ +int encode_context_boolean(uint8_t * apdu, int tag_number, + bool boolean_value) +{ + int len = 0; /* return value */ + + len = encode_tag(&apdu[0], (uint8_t) tag_number, true, 1); + apdu[len] = boolean_value ? 1 : 0; + len++; + + return len; +} + +bool decode_context_boolean(uint8_t * apdu) +{ + bool boolean_value = false; + + if (apdu[0]) + boolean_value = true; + + return boolean_value; +} + +/* from clause 20.2.3 Encoding of a Boolean Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +bool decode_boolean(uint32_t len_value) +{ + bool boolean_value = false; + + if (len_value) + boolean_value = true; + + return boolean_value; +} + +/* from clause 20.2.2 Encoding of a Null Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tagged_null(uint8_t * apdu) +{ + return encode_tag(&apdu[0], BACNET_APPLICATION_TAG_NULL, false, 0); +} + +int encode_context_null(uint8_t * apdu, int tag_number) +{ + return encode_tag(&apdu[0], (uint8_t)tag_number, true, 0); +} + +static uint8_t byte_reverse_bits(uint8_t in_byte) +{ + uint8_t out_byte = 0; + + if (in_byte & BIT0) + out_byte |= BIT7; + if (in_byte & BIT1) + out_byte |= BIT6; + if (in_byte & BIT2) + out_byte |= BIT5; + if (in_byte & BIT3) + out_byte |= BIT4; + if (in_byte & BIT4) + out_byte |= BIT3; + if (in_byte & BIT5) + out_byte |= BIT2; + if (in_byte & BIT6) + out_byte |= BIT1; + if (in_byte & BIT7) + out_byte |= BIT0; + + return out_byte; +} + +/* from clause 20.2.10 Encoding of a Bit String Value */ +/* returns the number of apdu bytes consumed */ +int decode_bitstring(uint8_t * apdu, uint32_t len_value, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint8_t unused_bits = 0; + uint32_t i = 0; + uint32_t bytes_used = 0; + + + bitstring_init(bit_string); + if (len_value) { + /* the first octet contains the unused bits */ + bytes_used = len_value - 1; + if (bytes_used <= MAX_BITSTRING_BYTES) { + len = 1; + for (i = 0; i < bytes_used; i++) { + bitstring_set_octet(bit_string, (uint8_t) i, + byte_reverse_bits(apdu[len++])); + } + unused_bits = apdu[0] & 0x07; + bitstring_set_bits_used(bit_string, + (uint8_t) bytes_used, unused_bits); + } + } + + return len; +} + +/* from clause 20.2.10 Encoding of a Bit String Value */ +/* returns the number of apdu bytes consumed */ +int encode_bitstring(uint8_t * apdu, BACNET_BIT_STRING * bit_string) +{ + int len = 0; + uint8_t remaining_used_bits = 0; + uint8_t used_bytes = 0; + uint8_t i = 0; + + /* if the bit string is empty, then the first octet shall be zero */ + if (bitstring_bits_used(bit_string) == 0) + apdu[len++] = 0; + else { + used_bytes = bitstring_bytes_used(bit_string); + remaining_used_bits = bitstring_bits_used(bit_string) - + ((used_bytes - 1) * 8); + /* number of unused bits in the subsequent final octet */ + apdu[len++] = 8 - remaining_used_bits; + for (i = 0; i < used_bytes; i++) { + apdu[len++] = + byte_reverse_bits(bitstring_octet(bit_string, i)); + } + } + + return len; +} + +int encode_tagged_bitstring(uint8_t * apdu, BACNET_BIT_STRING * bit_string) +{ + int len = 0; + int bit_string_encoded_length = 1; /* 1 for the bits remaining octet */ + + /* bit string may use more than 1 octet for the tag, so find out how many */ + bit_string_encoded_length += bitstring_bytes_used(bit_string); + len = encode_tag(&apdu[0], BACNET_APPLICATION_TAG_BIT_STRING, false, + bit_string_encoded_length); + len += encode_bitstring(&apdu[len], bit_string); + + return len; +} + +int encode_context_bitstring(uint8_t * apdu, int tag_number, + BACNET_BIT_STRING * bit_string) +{ + int len = 0; + int bit_string_encoded_length = 1; /* 1 for the bits remaining octet */ + + /* bit string may use more than 1 octet for the tag, so find out how many */ + bit_string_encoded_length += bitstring_bytes_used(bit_string); + len = + encode_tag(&apdu[0], (uint8_t)tag_number, true, bit_string_encoded_length); + len += encode_bitstring(&apdu[len], bit_string); + + return len; +} + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* returns the number of apdu bytes consumed */ +int decode_real(uint8_t * apdu, float *real_value) +{ + union { + uint8_t byte[4]; + float real_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ +#if BIG_ENDIAN + my_data.byte[0] = apdu[0]; + my_data.byte[1] = apdu[1]; + my_data.byte[2] = apdu[2]; + my_data.byte[3] = apdu[3]; +#else + my_data.byte[0] = apdu[3]; + my_data.byte[1] = apdu[2]; + my_data.byte[2] = apdu[1]; + my_data.byte[3] = apdu[0]; +#endif + + *real_value = my_data.real_value; + + return 4; +} + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_real(float value, uint8_t * apdu) +{ + union { + uint8_t byte[4]; + float real_value; + } my_data; + + /* NOTE: assumes the compiler stores float as IEEE-754 float */ + my_data.real_value = value; +#if BIG_ENDIAN + apdu[0] = my_data.byte[0]; + apdu[1] = my_data.byte[1]; + apdu[2] = my_data.byte[2]; + apdu[3] = my_data.byte[3]; +#else + apdu[0] = my_data.byte[3]; + apdu[1] = my_data.byte[2]; + apdu[2] = my_data.byte[1]; + apdu[3] = my_data.byte[0]; +#endif + + return 4; +} + +/* from clause 20.2.6 Encoding of a Real Number Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tagged_real(uint8_t * apdu, float value) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_real(value, &apdu[1]); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_REAL, false, len); + + return len; +} + +int encode_context_real(uint8_t * apdu, int tag_number, float value) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_real(value, &apdu[1]); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if (tag_number <= 14) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* returns the number of apdu bytes consumed */ +int decode_object_id(uint8_t * apdu, int *object_type, uint32_t * instance) +{ + uint32_t value = 0; + int len = 0; + + len = decode_unsigned32(apdu, &value); + *object_type = ((value >> BACNET_INSTANCE_BITS) & BACNET_MAX_OBJECT); + *instance = (value & BACNET_MAX_INSTANCE); + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_object_id(uint8_t * apdu, + int object_type, uint32_t instance) +{ + uint32_t value = 0; + uint32_t type = 0; + int len = 0; + + type = object_type; + value = ((type & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | + (instance & BACNET_MAX_INSTANCE); + len = encode_unsigned32(apdu, value); + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_object_id(uint8_t * apdu, + int tag_number, int object_type, uint32_t instance) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_object_id(&apdu[1], object_type, instance); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* from clause 20.2.14 Encoding of an Object Identifier Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_tagged_object_id(uint8_t * apdu, + int object_type, uint32_t instance) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_object_id(&apdu[1], object_type, instance); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OBJECT_ID, + false, len); + + return len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* returns the number of apdu bytes consumed */ +int encode_octet_string(uint8_t * apdu, BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + uint8_t *value; + int i = 0; /* loop counter */ + + if (octet_string) { + /* FIXME: might need to pass in the length of the APDU + to bounds check since it might not be the only data chunk */ + len = octetstring_length(octet_string); + value = octetstring_value(octet_string); + for (i = 0; i < len; i++) { + apdu[i] = value[i]; + } + } + + return len; +} + +/* 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_tagged_octet_string(uint8_t * apdu, + BACNET_OCTET_STRING * octet_string) +{ + int apdu_len = 0; + + if (octet_string) { + apdu_len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_OCTET_STRING, + false, octetstring_length(octet_string)); + /* FIXME: probably need to pass in the length of the APDU + to bounds check since it might not be the only data chunk */ + if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) + apdu_len += encode_octet_string(&apdu[apdu_len], octet_string); + else + apdu_len = 0; + } + + return apdu_len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int encode_context_octet_string(uint8_t * apdu, + int tag_number, BACNET_OCTET_STRING * octet_string) +{ + int apdu_len = 0; + + if (apdu && octet_string) { + apdu_len = encode_tag(&apdu[0], (uint8_t) tag_number, + true, octetstring_length(octet_string)); + if ((apdu_len + octetstring_length(octet_string)) < MAX_APDU) + apdu_len += encode_octet_string(&apdu[apdu_len], octet_string); + else + apdu_len = 0; + } + + return apdu_len; +} + +/* from clause 20.2.8 Encoding of an Octet String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_octet_string(uint8_t * apdu, uint32_t len_value, + BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + bool status = false; + + status = octetstring_init(octet_string, &apdu[0], len_value); + if (status) + len = len_value; + + return len; +} + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* returns the number of apdu bytes consumed */ +int encode_bacnet_character_string(uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string) +{ + int len, i; + char *pString; + + len = characterstring_length(char_string); + apdu[0] = characterstring_encoding(char_string); + pString = characterstring_value(char_string); + for (i = 0; i < len; i++) { + apdu[1 + i] = pString[i]; + } + + return len + 1 /* for encoding */ ; +} + +/* 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_tagged_character_string(uint8_t * apdu, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; + int string_len = 0; + + string_len = + characterstring_length(char_string) + 1 /* for encoding */ ; + len = + encode_tag(&apdu[0], BACNET_APPLICATION_TAG_CHARACTER_STRING, + false, string_len); + if ((len + string_len) < MAX_APDU) + len += encode_bacnet_character_string(&apdu[len], char_string); + else + len = 0; + + return len; +} + +int encode_context_character_string(uint8_t * apdu, int tag_number, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; + int string_len = 0; + + string_len = + characterstring_length(char_string) + 1 /* for encoding */ ; + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, string_len); + if ((len + string_len) < MAX_APDU) + len += encode_bacnet_character_string(&apdu[len], char_string); + else + len = 0; + + return len; +} + +/* from clause 20.2.9 Encoding of a Character String Value */ +/* and 20.2.1 General Rules for Encoding BACnet Tags */ +/* returns the number of apdu bytes consumed */ +int decode_character_string(uint8_t * apdu, uint32_t len_value, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; /* return value */ + bool status = false; + + status = characterstring_init(char_string, apdu[0], (char *) &apdu[1], + len_value - 1); + if (status) + len = len_value; + + return len; +} + +/* 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 len = 0; /* return value */ + + if (value < 0x100) { + apdu[0] = (uint8_t) value; + len = 1; + } else if (value < 0x10000) { + len = encode_unsigned16(&apdu[0], (uint16_t) value); + } else if (value < 0x1000000) { + len = encode_unsigned24(&apdu[0], value); + } else { + len = encode_unsigned32(&apdu[0], value); + } + + return len; +} + +/* 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_context_unsigned(uint8_t * apdu, int tag_number, uint32_t value) +{ + int len = 0; + + len = encode_bacnet_unsigned(&apdu[1], value); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* 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_tagged_unsigned(uint8_t * apdu, uint32_t value) +{ + int len = 0; + + len = encode_bacnet_unsigned(&apdu[1], value); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_UNSIGNED_INT, + false, len); + + return len; +} + +/* 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 decode_unsigned(uint8_t * apdu, uint32_t len_value, uint32_t * value) +{ + uint16_t unsigned16_value = 0; + + if (value) { + switch (len_value) { + case 1: + *value = apdu[0]; + break; + case 2: + decode_unsigned16(&apdu[0], &unsigned16_value); + *value = unsigned16_value; + break; + case 3: + decode_unsigned24(&apdu[0], value); + break; + case 4: + decode_unsigned32(&apdu[0], value); + break; + default: + *value = 0; + break; + } + } + + return len_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) +{ + uint32_t unsigned_value = 0; + int len; + + len = decode_unsigned(apdu, len_value, &unsigned_value); + if (value) + *value = unsigned_value; + + return len; +} + +/* 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 encode_bacnet_enumerated(uint8_t * apdu, int value) +{ + return encode_bacnet_unsigned(apdu, 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 encode_tagged_enumerated(uint8_t * apdu, int value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_enumerated(&apdu[1], value); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_ENUMERATED, + false, len); + + return len; +} + +/* 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 encode_context_enumerated(uint8_t * apdu, int tag_number, int value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_enumerated(&apdu[1], value); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* 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 decode_signed(uint8_t * apdu, uint32_t len_value, int32_t * value) +{ + int8_t signed8_value = 0; + int16_t signed16_value = 0; + + if (value) { + switch (len_value) { + case 1: + decode_signed8(&apdu[0], &signed8_value); + *value = signed8_value; + break; + case 2: + decode_signed16(&apdu[0], &signed16_value); + *value = signed16_value; + break; + case 3: + decode_signed24(&apdu[0], value); + break; + case 4: + decode_signed32(&apdu[0], value); + break; + default: + *value = 0; + break; + } + } + + return len_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 len = 0; /* return value */ + + /* don't encode the leading X'FF' or X'00' of the two's compliment. + That is, the first octet of any multi-octet encoded value shall + not be X'00' if the most significant bit (bit 7) of the second + octet is 0, and the first octet shall not be X'FF' if the most + significant bit of the second octet is 1. */ + if ((value >= -128) && (value < 128)) { + len = encode_signed8(&apdu[0], (int8_t) value); + } else if ((value >= -32768) && (value < 32768)) { + len = encode_signed16(&apdu[0], (int16_t) value); + } else if ((value > -8388608) && (value < 8388608)) { + len = encode_signed24(&apdu[0], value); + } else { + len = encode_signed32(&apdu[0], value); + } + + return len; +} + +/* 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_tagged_signed(uint8_t * apdu, int32_t value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_signed(&apdu[1], value); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_SIGNED_INT, + false, len); + + return len; +} + +/* 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_context_signed(uint8_t * apdu, int tag_number, int32_t value) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_signed(&apdu[1], value); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* 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) +{ + apdu[0] = btime->hour; + apdu[1] = btime->min; + apdu[2] = btime->sec; + apdu[3] = btime->hundredths; + + return 4; +} + +/* 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_tagged_time(uint8_t * apdu, BACNET_TIME * btime) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_time(&apdu[1], btime); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_TIME, false, len); + + return len; +} + +int encode_context_time(uint8_t * apdu, int tag_number, + BACNET_TIME * btime) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_time(&apdu[1], btime); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* 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 decode_bacnet_time(uint8_t * apdu, BACNET_TIME * btime) +{ + btime->hour = apdu[0]; + btime->min = apdu[1]; + btime->sec = apdu[2]; + btime->hundredths = apdu[3]; + + return 4; +} + +/* 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) +{ + /* allow 2 digit years */ + if (bdate->year < 1900) { + if (bdate->year <= 38) + bdate->year += 2000; + else + bdate->year += 1900; + } + apdu[0] = bdate->year - 1900; + apdu[1] = bdate->month; + apdu[2] = bdate->day; + apdu[3] = bdate->wday; + + return 4; +} + +/* 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_tagged_date(uint8_t * apdu, BACNET_DATE * bdate) +{ + int len = 0; + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_date(&apdu[1], bdate); + len += encode_tag(&apdu[0], BACNET_APPLICATION_TAG_DATE, false, len); + + return len; + +} + +int encode_context_date(uint8_t * apdu, int tag_number, + BACNET_DATE * bdate) +{ + int len = 0; /* return value */ + + /* assumes that the tag only consumes 1 octet */ + len = encode_bacnet_date(&apdu[1], bdate); + /* we only reserved 1 byte for encoding the tag - check the limits */ + if ((tag_number <= 14) && (len <= 4)) + len += encode_tag(&apdu[0], (uint8_t) tag_number, true, len); + else + len = 0; + + return len; +} + +/* 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 decode_date(uint8_t * apdu, BACNET_DATE * bdate) +{ + bdate->year = apdu[0] + 1900; + bdate->month = apdu[1]; + bdate->day = apdu[2]; + bdate->wday = apdu[3]; + + return 4; +} + +/* returns the number of apdu bytes consumed */ +int encode_simple_ack(uint8_t * apdu, uint8_t invoke_id, + uint8_t service_choice) +{ + apdu[0] = PDU_TYPE_SIMPLE_ACK; + apdu[1] = invoke_id; + apdu[2] = service_choice; + + return 3; +} + +/* end of decoding_encoding.c */ +#ifdef TEST +#include +#include +#include +#include "ctest.h" + +static int get_apdu_len(bool extended_tag, uint32_t value) +{ + int test_len = 1; + + if (extended_tag) + test_len++; + if (value <= 4) + test_len += 0; /* do nothing... */ + else if (value <= 253) + test_len += 1; + else if (value <= 65535) + test_len += 3; + else + test_len += 5; + + return test_len; +} + +static void print_apdu(uint8_t * pBlock, uint32_t num) +{ + size_t lines = 0; /* number of lines to print */ + size_t line = 0; /* line of text counter */ + size_t last_line = 0; /* line on which the last text resided */ + unsigned long count = 0; /* address to print */ + unsigned int i = 0; /* counter */ + + if (pBlock && num) { + /* how many lines to print? */ + num--; /* adjust */ + lines = (num / 16) + 1; + last_line = num % 16; + + /* create the line */ + for (line = 0; line < lines; line++) { + /* start with the address */ + printf("%08lX: ", count); + /* hex representation */ + for (i = 0; i < 16; i++) { + if (((line == (lines - 1)) && (i <= last_line)) || + (line != (lines - 1))) { + printf("%02X ", (unsigned) (0x00FF & pBlock[i])); + } else + printf("-- "); + } + printf(" "); + /* print the characters if valid */ + for (i = 0; i < 16; i++) { + if (((line == (lines - 1)) && (i <= last_line)) || + (line != (lines - 1))) { + if (isprint(pBlock[i])) { + printf("%c", pBlock[i]); + } else + printf("."); + } else + printf("."); + } + printf("\r\n"); + pBlock += 16; + count += 16; + } + } + + return; +} + +void testBACDCodeTags(Test * pTest) +{ + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0, test_tag_number = 0; + int len = 0, test_len = 0; + uint32_t value = 0, test_value = 0; + + for (tag_number = 0;; tag_number++) { + len = encode_opening_tag(&apdu[0], tag_number); + test_len = + get_apdu_len(decode_is_extended_tag_number(&apdu[0]), 0); + ct_test(pTest, len == test_len); + len = + decode_tag_number_and_value(&apdu[0], &test_tag_number, + &value); + ct_test(pTest, value == 0); + ct_test(pTest, len == test_len); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, decode_is_opening_tag(&apdu[0]) == true); + ct_test(pTest, decode_is_closing_tag(&apdu[0]) == false); + len = encode_closing_tag(&apdu[0], tag_number); + ct_test(pTest, len == test_len); + len = + decode_tag_number_and_value(&apdu[0], &test_tag_number, + &value); + ct_test(pTest, len == test_len); + ct_test(pTest, value == 0); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, decode_is_opening_tag(&apdu[0]) == false); + ct_test(pTest, decode_is_closing_tag(&apdu[0]) == true); + /* test the len-value-type portion */ + for (value = 1;; value = value << 1) { + len = encode_tag(&apdu[0], tag_number, false, value); + len = decode_tag_number_and_value(&apdu[0], &test_tag_number, + &test_value); + ct_test(pTest, tag_number == test_tag_number); + ct_test(pTest, value == test_value); + test_len = + get_apdu_len(decode_is_extended_tag_number(&apdu[0]), + value); + ct_test(pTest, len == test_len); + /* stop at the the last value */ + if (value & BIT31) + break; + } + /* stop after the last tag number */ + if (tag_number == 255) + break; + } + + return; +} + +void testBACDCodeReal(Test * pTest) +{ + uint8_t real_array[4] = { 0 }; + uint8_t encoded_array[4] = { 0 }; + float value = 42.123; + float decoded_value = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + int len = 0, apdu_len = 0; + uint8_t tag_number = 0; + uint32_t long_value = 0; + + encode_bacnet_real(value, &real_array[0]); + decode_real(&real_array[0], &decoded_value); + ct_test(pTest, decoded_value == value); + encode_bacnet_real(value, &encoded_array[0]); + ct_test(pTest, memcmp(&real_array, &encoded_array, + sizeof(real_array)) == 0); + + /* a real will take up 4 octects plus a one octet tag */ + apdu_len = encode_tagged_real(&apdu[0], value); + ct_test(pTest, apdu_len == 5); + /* len tells us how many octets were used for encoding the value */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, &long_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_REAL); + ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + ct_test(pTest, len == 1); + ct_test(pTest, long_value == 4); + decode_real(&apdu[len], &decoded_value); + ct_test(pTest, decoded_value == value); + + return; +} + +void testBACDCodeEnumerated(Test * pTest) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + int value = 1; + int decoded_value = 0; + int i = 0, apdu_len = 0; + int len = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + for (i = 0; i < 31; i++) { + apdu_len = encode_tagged_enumerated(&array[0], value); + len = + decode_tag_number_and_value(&array[0], &tag_number, + &len_value); + len += decode_enumerated(&array[len], len_value, &decoded_value); + ct_test(pTest, decoded_value == value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_ENUMERATED); + ct_test(pTest, len == apdu_len); + /* encode back the value */ + encode_tagged_enumerated(&encoded_array[0], decoded_value); + ct_test(pTest, memcmp(&array[0], &encoded_array[0], + sizeof(array)) == 0); + /* an enumerated will take up to 4 octects */ + /* plus a one octet for the tag */ + apdu_len = encode_tagged_enumerated(&apdu[0], value); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_ENUMERATED); + ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + /* context specific encoding */ + apdu_len = encode_context_enumerated(&apdu[0], 3, value); + ct_test(pTest, decode_is_context_specific(&apdu[0]) == true); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == 3); + /* test the interesting values */ + value = value << 1; + } + + return; +} + +void testBACDCodeUnsignedValue(Test * pTest, uint32_t value) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + uint32_t decoded_value = 0; + int len, apdu_len; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + len_value = encode_tagged_unsigned(&array[0], value); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len = decode_unsigned(&array[len], len_value, &decoded_value); + ct_test(pTest, decoded_value == value); + if (decoded_value != value) { + printf("value=%u decoded_value=%u\n", value, decoded_value); + print_apdu(&array[0], sizeof(array)); + } + encode_tagged_unsigned(&encoded_array[0], decoded_value); + ct_test(pTest, memcmp(&array[0], &encoded_array[0], + sizeof(array)) == 0); + /* an unsigned will take up to 4 octects */ + /* plus a one octet for the tag */ + apdu_len = encode_tagged_unsigned(&apdu[0], value); + /* apdu_len varies... */ + /*ct_test(pTest, apdu_len == 5); */ + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, len == 1); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_UNSIGNED_INT); + ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); +} + +void testBACDCodeUnsigned(Test * pTest) +{ + uint32_t value = 1; + int i; + + for (i = 0; i < 32; i++) { + testBACDCodeUnsignedValue(pTest, value - 1); + testBACDCodeUnsignedValue(pTest, value); + testBACDCodeUnsignedValue(pTest, value + 1); + value = value << 1; + } + + return; +} + +void testBACDCodeSignedValue(Test * pTest, int32_t value) +{ + uint8_t array[5] = { 0 }; + uint8_t encoded_array[5] = { 0 }; + int32_t decoded_value = 0; + int len = 0, apdu_len = 0; + uint8_t apdu[MAX_APDU] = { 0 }; + uint8_t tag_number = 0; + uint32_t len_value = 0; + int diff = 0; + + len = encode_tagged_signed(&array[0], value); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + len = decode_signed(&array[len], len_value, &decoded_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT); + ct_test(pTest, decoded_value == value); + if (decoded_value != value) { + printf("value=%d decoded_value=%d\n", value, decoded_value); + print_apdu(&array[0], sizeof(array)); + } + encode_tagged_signed(&encoded_array[0], decoded_value); + diff = memcmp(&array[0], &encoded_array[0], sizeof(array)); + ct_test(pTest, diff == 0); + if (diff) { + printf("value=%d decoded_value=%d\n", value, decoded_value); + print_apdu(&array[0], sizeof(array)); + print_apdu(&encoded_array[0], sizeof(array)); + } + /* a signed int will take up to 4 octects */ + /* plus a one octet for the tag */ + apdu_len = encode_tagged_signed(&apdu[0], value); + len = decode_tag_number_and_value(&apdu[0], &tag_number, NULL); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_SIGNED_INT); + ct_test(pTest, decode_is_context_specific(&apdu[0]) == false); + + return; +} + +void testBACDCodeSigned(Test * pTest) +{ + int value = 1; + int i = 0; + + for (i = 0; i < 32; i++) { + testBACDCodeSignedValue(pTest, value - 1); + testBACDCodeSignedValue(pTest, value); + testBACDCodeSignedValue(pTest, value + 1); + value = value << 1; + } + + testBACDCodeSignedValue(pTest, -1); + value = -2; + for (i = 0; i < 32; i++) { + testBACDCodeSignedValue(pTest, value - 1); + testBACDCodeSignedValue(pTest, value); + testBACDCodeSignedValue(pTest, value + 1); + value = value << 1; + } + + return; +} + +void testBACDCodeOctetString(Test * pTest) +{ + uint8_t array[MAX_APDU] = { 0 }; + uint8_t encoded_array[MAX_APDU] = { 0 }; + BACNET_OCTET_STRING octet_string; + BACNET_OCTET_STRING test_octet_string; + uint8_t test_value[MAX_APDU] = { "" }; + int i; /* for loop counter */ + int apdu_len; + int len; + uint8_t tag_number = 0; + uint32_t len_value = 0; + bool status = false; + int diff = 0; /* for memcmp */ + + status = octetstring_init(&octet_string, NULL, 0); + ct_test(pTest, status == true); + apdu_len = encode_tagged_octet_string(&array[0], &octet_string); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OCTET_STRING); + len += decode_octet_string(&array[len], len_value, &test_octet_string); + ct_test(pTest, apdu_len == len); + diff = memcmp(octetstring_value(&octet_string), &test_value[0], + octetstring_length(&octet_string)); + ct_test(pTest, diff == 0); + + for (i = 0; i < (MAX_APDU - 6); i++) { + test_value[i] = '0' + (i % 10); + status = octetstring_init(&octet_string, test_value, i); + ct_test(pTest, status == true); + apdu_len = + encode_tagged_octet_string(&encoded_array[0], &octet_string); + len = + decode_tag_number_and_value(&encoded_array[0], &tag_number, + &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_OCTET_STRING); + len += decode_octet_string(&encoded_array[len], len_value, + &test_octet_string); + if (apdu_len != len) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, apdu_len == len); + diff = memcmp(octetstring_value(&octet_string), &test_value[0], + octetstring_length(&octet_string)); + if (diff) { + printf("test octet string=#%d\n", i); + } + ct_test(pTest, diff == 0); + } + + return; +} + +void testBACDCodeCharacterString(Test * pTest) +{ + uint8_t array[MAX_APDU] = { 0 }; + uint8_t encoded_array[MAX_APDU] = { 0 }; + BACNET_CHARACTER_STRING char_string; + BACNET_CHARACTER_STRING test_char_string; + char test_value[MAX_APDU] = { "" }; + int i; /* for loop counter */ + int apdu_len; + int len; + uint8_t tag_number = 0; + uint32_t len_value = 0; + int diff = 0; /* for comparison */ + bool status = false; + + status = characterstring_init(&char_string, + CHARACTER_ANSI_X34, NULL, 0); + ct_test(pTest, status == true); + apdu_len = encode_tagged_character_string(&array[0], &char_string); + len = decode_tag_number_and_value(&array[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING); + len += + decode_character_string(&array[len], len_value, &test_char_string); + ct_test(pTest, apdu_len == len); + diff = memcmp(characterstring_value(&char_string), &test_value[0], + characterstring_length(&char_string)); + ct_test(pTest, diff == 0); + for (i = 0; i < MAX_CHARACTER_STRING_BYTES - 1; i++) { + test_value[i] = 'S'; + test_value[i + 1] = '\0'; + status = characterstring_init_ansi(&char_string, test_value); + ct_test(pTest, status == true); + apdu_len = + encode_tagged_character_string(&encoded_array[0], + &char_string); + len = + decode_tag_number_and_value(&encoded_array[0], &tag_number, + &len_value); + ct_test(pTest, + tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING); + len += + decode_character_string(&encoded_array[len], len_value, + &test_char_string); + if (apdu_len != len) { + printf("test string=#%d apdu_len=%d len=%d\n", i, apdu_len, + len); + } + ct_test(pTest, apdu_len == len); + diff = memcmp(characterstring_value(&char_string), &test_value[0], + characterstring_length(&char_string)); + if (diff) { + printf("test string=#%d\n", i); + } + ct_test(pTest, diff == 0); + } + + return; +} + +void testBACDCodeObject(Test * pTest) +{ + uint8_t object_array[4] = { + 0 + }; + uint8_t encoded_array[4] = { + 0 + }; + BACNET_OBJECT_TYPE type = OBJECT_BINARY_INPUT; + BACNET_OBJECT_TYPE decoded_type = OBJECT_ANALOG_OUTPUT; + uint32_t instance = 123; + uint32_t decoded_instance = 0; + + encode_bacnet_object_id(&encoded_array[0], type, instance); + decode_object_id(&encoded_array[0], + (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + encode_bacnet_object_id(&object_array[0], type, instance); + ct_test(pTest, memcmp(&object_array[0], &encoded_array[0], + sizeof(object_array)) == 0); + for (type = 0; type < 1024; type++) { + for (instance = 0; instance <= BACNET_MAX_INSTANCE; + instance += 1024) { + encode_bacnet_object_id(&encoded_array[0], type, instance); + decode_object_id(&encoded_array[0], + (int *) &decoded_type, &decoded_instance); + ct_test(pTest, decoded_type == type); + ct_test(pTest, decoded_instance == instance); + encode_bacnet_object_id(&object_array[0], type, instance); + ct_test(pTest, memcmp(&object_array[0], + &encoded_array[0], sizeof(object_array)) == 0); + } + } + + return; +} + +void testBACDCodeMaxSegsApdu(Test * pTest) +{ + int max_segs[8] = { 0, 2, 4, 8, 16, 32, 64, 65 }; + int max_apdu[6] = { 50, 128, 206, 480, 1024, 1476 }; + int i = 0; + int j = 0; + uint8_t octet = 0; + + /* test */ + for (i = 0; i < 8; i++) { + for (j = 0; j < 6; j++) { + octet = encode_max_segs_max_apdu(max_segs[i], max_apdu[j]); + ct_test(pTest, max_segs[i] == decode_max_segs(octet)); + ct_test(pTest, max_apdu[j] == decode_max_apdu(octet)); + } + } +} + +void testBACDCodeBitString(Test * pTest) +{ + uint8_t bit = 0; + BACNET_BIT_STRING bit_string; + BACNET_BIT_STRING decoded_bit_string; + uint8_t apdu[MAX_APDU] = { 0 }; + uint32_t len_value = 0; + uint8_t tag_number = 0; + int len = 0; + + 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 encode/decode -- 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); + /* encode */ + len = encode_tagged_bitstring(&apdu[0], &bit_string); + /* decode */ + len = + decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_BIT_STRING); + len += + decode_bitstring(&apdu[len], len_value, &decoded_bit_string); + ct_test(pTest, + bitstring_bits_used(&decoded_bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&decoded_bit_string, bit) == true); + } + /* test encode/decode -- 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); + /* encode */ + len = encode_tagged_bitstring(&apdu[0], &bit_string); + /* decode */ + len = + decode_tag_number_and_value(&apdu[0], &tag_number, &len_value); + ct_test(pTest, tag_number == BACNET_APPLICATION_TAG_BIT_STRING); + len += + decode_bitstring(&apdu[len], len_value, &decoded_bit_string); + ct_test(pTest, + bitstring_bits_used(&decoded_bit_string) == (bit + 1)); + ct_test(pTest, bitstring_bit(&decoded_bit_string, bit) == false); + } +} + +#ifdef TEST_DECODE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACDCode", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBACDCodeTags); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeReal); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeUnsigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeSigned); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeEnumerated); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeOctetString); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeCharacterString); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeObject); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeMaxSegsApdu); + assert(rc); + rc = ct_addTestFunction(pTest, testBACDCodeBitString); + assert(rc); + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DECODE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/bacdcode.h b/bacnet-stack-0-3-0/bacdcode.h new file mode 100644 index 00000000..07bc3302 --- /dev/null +++ b/bacnet-stack-0-3-0/bacdcode.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/bacdcode.ide b/bacnet-stack-0-3-0/bacdcode.ide new file mode 100644 index 00000000..9dabaa4b Binary files /dev/null and b/bacnet-stack-0-3-0/bacdcode.ide differ diff --git a/bacnet-stack-0-3-0/bacdcode.mak b/bacnet-stack-0-3-0/bacdcode.mak new file mode 100644 index 00000000..04e84013 --- /dev/null +++ b/bacnet-stack-0-3-0/bacdcode.mak @@ -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 diff --git a/bacnet-stack-0-3-0/bacdef.h b/bacnet-stack-0-3-0/bacdef.h new file mode 100644 index 00000000..dc7efe04 --- /dev/null +++ b/bacnet-stack-0-3-0/bacdef.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/bacenum.h b/bacnet-stack-0-3-0/bacenum.h new file mode 100644 index 00000000..fb50dfe7 --- /dev/null +++ b/bacnet-stack-0-3-0/bacenum.h @@ -0,0 +1,1209 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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 BACENUM_H +#define BACENUM_H + +typedef enum { + PROP_ACKED_TRANSITIONS = 0, + PROP_ACK_REQUIRED = 1, + PROP_ACTION = 2, + PROP_ACTION_TEXT = 3, + PROP_ACTIVE_TEXT = 4, + PROP_ACTIVE_VT_SESSIONS = 5, + PROP_ALARM_VALUE = 6, + PROP_ALARM_VALUES = 7, + PROP_ALL = 8, + PROP_ALL_WRITES_SUCCESSFUL = 9, + PROP_APDU_SEGMENT_TIMEOUT = 10, + PROP_APDU_TIMEOUT = 11, + PROP_APPLICATION_SOFTWARE_VERSION = 12, + PROP_ARCHIVE = 13, + PROP_BIAS = 14, + PROP_CHANGE_OF_STATE_COUNT = 15, + PROP_CHANGE_OF_STATE_TIME = 16, + PROP_NOTIFICATION_CLASS = 17, + PROP_BLANK_1 = 18, + PROP_CONTROLLED_VARIABLE_REFERENCE = 19, + PROP_CONTROLLED_VARIABLE_UNITS = 20, + PROP_CONTROLLED_VARIABLE_VALUE = 21, + PROP_COV_INCREMENT = 22, + PROP_DATE_LIST = 23, + PROP_DAYLIGHT_SAVINGS_STATUS = 24, + PROP_DEADBAND = 25, + PROP_DERIVATIVE_CONSTANT = 26, + PROP_DERIVATIVE_CONSTANT_UNITS = 27, + PROP_DESCRIPTION = 28, + PROP_DESCRIPTION_OF_HALT = 29, + PROP_DEVICE_ADDRESS_BINDING = 30, + PROP_DEVICE_TYPE = 31, + PROP_EFFECTIVE_PERIOD = 32, + PROP_ELAPSED_ACTIVE_TIME = 33, + PROP_ERROR_LIMIT = 34, + PROP_EVENT_ENABLE = 35, + PROP_EVENT_STATE = 36, + PROP_EVENT_TYPE = 37, + PROP_EXCEPTION_SCHEDULE = 38, + PROP_FAULT_VALUES = 39, + PROP_FEEDBACK_VALUE = 40, + PROP_FILE_ACCESS_METHOD = 41, + PROP_FILE_SIZE = 42, + PROP_FILE_TYPE = 43, + PROP_FIRMWARE_REVISION = 44, + PROP_HIGH_LIMIT = 45, + PROP_INACTIVE_TEXT = 46, + PROP_IN_PROCESS = 47, + PROP_INSTANCE_OF = 48, + PROP_INTEGRAL_CONSTANT = 49, + PROP_INTEGRAL_CONSTANT_UNITS = 50, + PROP_ISSUE_CONFIRMED_NOTIFICATIONS = 51, + PROP_LIMIT_ENABLE = 52, + PROP_LIST_OF_GROUP_MEMBERS = 53, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES = 54, + PROP_LIST_OF_SESSION_KEYS = 55, + PROP_LOCAL_DATE = 56, + PROP_LOCAL_TIME = 57, + PROP_LOCATION = 58, + PROP_LOW_LIMIT = 59, + PROP_MANIPULATED_VARIABLE_REFERENCE = 60, + PROP_MAXIMUM_OUTPUT = 61, + PROP_MAX_APDU_LENGTH_ACCEPTED = 62, + PROP_MAX_INFO_FRAMES = 63, + PROP_MAX_MASTER = 64, + PROP_MAX_PRES_VALUE = 65, + PROP_MINIMUM_OFF_TIME = 66, + PROP_MINIMUM_ON_TIME = 67, + PROP_MINIMUM_OUTPUT = 68, + PROP_MIN_PRES_VALUE = 69, + PROP_MODEL_NAME = 70, + PROP_MODIFICATION_DATE = 71, + PROP_NOTIFY_TYPE = 72, + PROP_NUMBER_OF_APDU_RETRIES = 73, + PROP_NUMBER_OF_STATES = 74, + PROP_OBJECT_IDENTIFIER = 75, + PROP_OBJECT_LIST = 76, + PROP_OBJECT_NAME = 77, + PROP_OBJECT_PROPERTY_REFERENCE = 78, + PROP_OBJECT_TYPE = 79, + PROP_OPTIONAL = 80, + PROP_OUT_OF_SERVICE = 81, + PROP_OUTPUT_UNITS = 82, + PROP_EVENT_PARAMETERS = 83, + PROP_POLARITY = 84, + PROP_PRESENT_VALUE = 85, + PROP_PRIORITY = 86, + PROP_PRIORITY_ARRAY = 87, + PROP_PRIORITY_FOR_WRITING = 88, + PROP_PROCESS_IDENTIFIER = 89, + PROP_PROGRAM_CHANGE = 90, + PROP_PROGRAM_LOCATION = 91, + PROP_PROGRAM_STATE = 92, + PROP_PROPORTIONAL_CONSTANT = 93, + PROP_PROPORTIONAL_CONSTANT_UNITS = 94, + PROP_PROTOCOL_CONFORMANCE_CLASS = 95, /* deleted in version 1 revision 2 */ + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED = 96, + PROP_PROTOCOL_SERVICES_SUPPORTED = 97, + PROP_PROTOCOL_VERSION = 98, + PROP_READ_ONLY = 99, + PROP_REASON_FOR_HALT = 100, + PROP_RECIPIENT = 101, + PROP_RECIPIENT_LIST = 102, + PROP_RELIABILITY = 103, + PROP_RELINQUISH_DEFAULT = 104, + PROP_REQUIRED = 105, + PROP_RESOLUTION = 106, + PROP_SEGMENTATION_SUPPORTED = 107, + PROP_SETPOINT = 108, + PROP_SETPOINT_REFERENCE = 109, + PROP_STATE_TEXT = 110, + PROP_STATUS_FLAGS = 111, + PROP_SYSTEM_STATUS = 112, + PROP_TIME_DELAY = 113, + PROP_TIME_OF_ACTIVE_TIME_RESET = 114, + PROP_TIME_OF_STATE_COUNT_RESET = 115, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS = 116, + PROP_UNITS = 117, + PROP_UPDATE_INTERVAL = 118, + PROP_UTC_OFFSET = 119, + PROP_VENDOR_IDENTIFIER = 120, + PROP_VENDOR_NAME = 121, + PROP_VT_CLASSES_SUPPORTED = 122, + PROP_WEEKLY_SCHEDULE = 123, + PROP_ATTEMPTED_SAMPLES = 124, + PROP_AVERAGE_VALUE = 125, + PROP_BUFFER_SIZE = 126, + PROP_CLIENT_COV_INCREMENT = 127, + PROP_COV_RESUBSCRIPTION_INTERVAL = 128, + PROP_CURRENT_NOTIFY_TIME = 129, + PROP_EVENT_TIME_STAMPS = 130, + PROP_LOG_BUFFER = 131, + PROP_LOG_DEVICE_OBJECT = 132, + /* The enable property is renamed from log-enable in + Addendum b to ANSI/ASHRAE 135-2004(135b-2) */ + PROP_ENABLE = 133, + PROP_LOG_INTERVAL = 134, + PROP_MAXIMUM_VALUE = 135, + PROP_MINIMUM_VALUE = 136, + PROP_NOTIFICATION_THRESHOLD = 137, + PROP_PREVIOUS_NOTIFY_TIME = 138, + PROP_PROTOCOL_REVISION = 139, + PROP_RECORDS_SINCE_NOTIFICATION = 140, + PROP_RECORD_COUNT = 141, + PROP_START_TIME = 142, + PROP_STOP_TIME = 143, + PROP_STOP_WHEN_FULL = 144, + PROP_TOTAL_RECORD_COUNT = 145, + PROP_VALID_SAMPLES = 146, + PROP_WINDOW_INTERVAL = 147, + PROP_WINDOW_SAMPLES = 148, + PROP_MAXIMUM_VALUE_TIMESTAMP = 149, + PROP_MINIMUM_VALUE_TIMESTAMP = 150, + PROP_VARIANCE_VALUE = 151, + PROP_ACTIVE_COV_SUBSCRIPTIONS = 152, + PROP_BACKUP_FAILURE_TIMEOUT = 153, + PROP_CONFIGURATION_FILES = 154, + PROP_DATABASE_REVISION = 155, + PROP_DIRECT_READING = 156, + PROP_LAST_RESTORE_TIME = 157, + PROP_MAINTENANCE_REQUIRED = 158, + PROP_MEMBER_OF = 159, + PROP_MODE = 160, + PROP_OPERATION_EXPECTED = 161, + PROP_SETTING = 162, + PROP_SILENCED = 163, + PROP_TRACKING_VALUE = 164, + PROP_ZONE_MEMBERS = 165, + PROP_LIFE_SAFETY_ALARM_VALUES = 166, + PROP_MAX_SEGMENTS_ACCEPTED = 167, + PROP_PROFILE_NAME = 168, + PROP_AUTO_SLAVE_DISCOVERY = 169, + PROP_MANUAL_SLAVE_ADDRESS_BINDING = 170, + PROP_SLAVE_ADDRESS_BINDING = 171, + PROP_SLAVE_PROXY_ENABLE = 172, + PROP_LAST_NOTIFY_TIME = 173, + PROP_SCHEDULE_DEFAULT = 174, + PROP_ACCEPTED_MODES = 175, + PROP_ADJUST_VALUE = 176, + PROP_COUNT = 177, + PROP_COUNT_BEFORE_CHANGE = 178, + PROP_COUNT_CHANGE_TIME = 179, + PROP_COV_PERIOD = 180, + PROP_INPUT_REFERENCE = 181, + PROP_LIMIT_MONITORING_INTERVAL = 182, + PROP_LOGGING_DEVICE = 183, + PROP_LOGGING_RECORD = 184, + PROP_PRESCALE = 185, + PROP_PULSE_RATE = 186, + PROP_SCALE = 187, + PROP_SCALE_FACTOR = 188, + PROP_UPDATE_TIME = 189, + PROP_VALUE_BEFORE_CHANGE = 190, + PROP_VALUE_SET = 191, + PROP_VALUE_CHANGE_TIME = 192, + /* enumerations 193-206 are new */ + PROP_ALIGN_INTERVALS = 193, + PROP_GROUP_MEMBER_NAMES = 194, + PROP_INTERVAL_OFFSET = 195, + PROP_LAST_RESTART_REASON = 196, + PROP_LOGGING_TYPE = 197, + PROP_MEMBER_STATUS_FLAGS = 198, + PROP_NOTIFICATION_PERIOD = 199, + PROP_PREVIOUS_NOTIFY_RECORD = 200, + PROP_REQUESTED_UPDATE_INTERVAL = 201, + PROP_RESTART_NOTIFICATION_RECIPIENTS = 202, + PROP_TIME_OF_DEVICE_RESTART = 203, + PROP_TIME_SYNCHRONIZATION_INTERVAL = 204, + PROP_TRIGGER = 205, + PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS = 206, + /* enumerations 207-211 are used in Addendum d to + ANSI/ASHRAE 135-2004 */ + PROP_NODE_SUBTYPE = 207, + PROP_NODE_TYPE = 208, + PROP_STRUCTURED_OBJECT_LIST = 209, + PROP_SUBORDINATE_ANNOTATIONS = 210, + PROP_SUBORDINATE_LIST = 211, + /* enumerations 212-225 are used in Addendum e to + ANSI/ASHRAE 135-2004 */ + PROP_ACTUAL_SHED_LEVEL = 212, + PROP_DUTY_WINDOW = 213, + PROP_EXPECTED_SHED_LEVEL = 214, + PROP_FULL_DUTY_BASELINE = 215, + /* note: missing enumerations for now */ + PROP_REQUESTED_SHED_LEVEL = 218, + PROP_SHED_DURATION = 219, + PROP_SHED_LEVEL_DESCRIPTIONS = 220, + PROP_SHED_LEVELS = 221, + PROP_STATE_DESCRIPTION = 222, + /* note: missing enumerations for now */ + /* enumerations 226-235 are used in Addendum f to + ANSI/ASHRAE 135-2004 */ + PROP_LOG_DEVICE_OBJECT_PROPERTIES = 236, + PROP_LOG_MULTIPLE_BUFFER = 237, + /* The special property identifiers all, optional, and required */ + /* are reserved for use in the ReadPropertyConditional and */ + /* ReadPropertyMultiple services or services not defined in this standard. */ + /* Enumerated values 0-511 are reserved for definition by ASHRAE. */ + /* Enumerated values 512-4194303 may be used by others subject to the */ + /* procedures and constraints described in Clause 23. */ + /* The highest enumeration used in this version is 168. */ + MAX_BACNET_PROPERTY_ID = 4194303 +} BACNET_PROPERTY_ID; + +typedef enum { + ACTION_DIRECT = 0, + ACTION_REVERSE = 1 +} BACNET_ACTION; + +typedef enum { + MIN_BINARY_PV = 0, /* for validating incoming values */ + BINARY_INACTIVE = 0, + BINARY_ACTIVE = 1, + MAX_BINARY_PV = 1, /* for validating incoming values */ + BINARY_NULL = 2 /* our homemade way of storing this info */ +} BACNET_BINARY_PV; + +typedef enum { + ACTION_BINARY_PV, + ACTION_UNSIGNED, + ACTION_FLOAT +} BACNET_ACTION_VALUE_TYPE; + +typedef enum { + EVENT_STATE_NORMAL = 0, + EVENT_STATE_FAULT = 1, + EVENT_STATE_OFFNORMAL = 2, + EVENT_STATE_HIGH_LIMIT = 3, + EVENT_STATE_LOW_LIMIT = 4 +} BACNET_EVENT_STATE; + +typedef enum { + STATUS_OPERATIONAL = 0, + STATUS_OPERATIONAL_READ_ONLY = 1, + STATUS_DOWNLOAD_REQUIRED = 2, + STATUS_DOWNLOAD_IN_PROGRESS = 3, + STATUS_NON_OPERATIONAL = 4, + MAX_DEVICE_STATUS = 5 +} BACNET_DEVICE_STATUS; + +typedef enum { + /* Acceleration */ + UNITS_METERS_PER_SECOND_PER_SECOND = 166, + /* Area */ + UNITS_SQUARE_METERS = 0, + UNITS_SQUARE_CENTIMETERS = 116, + UNITS_SQUARE_FEET = 1, + UNITS_SQUARE_INCHES = 115, + /* Currency */ + UNITS_CURRENCY1 = 105, + UNITS_CURRENCY2 = 106, + UNITS_CURRENCY3 = 107, + UNITS_CURRENCY4 = 108, + UNITS_CURRENCY5 = 109, + UNITS_CURRENCY6 = 110, + UNITS_CURRENCY7 = 111, + UNITS_CURRENCY8 = 112, + UNITS_CURRENCY9 = 113, + UNITS_CURRENCY10 = 114, + /* Electrical */ + UNITS_MILLIAMPERES = 2, + UNITS_AMPERES = 3, + UNITS_AMPERES_PER_METER = 167, + UNITS_AMPERES_PER_SQUARE_METER = 168, + UNITS_AMPERE_SQUARE_METERS = 169, + UNITS_FARADS = 170, + UNITS_HENRYS = 171, + UNITS_OHMS = 4, + UNITS_OHM_METERS = 172, + UNITS_MILLIOHMS = 145, + UNITS_KILOHMS = 122, + UNITS_MEGOHMS = 123, + UNITS_SIEMENS = 173, /* 1 mho equals 1 siemens */ + UNITS_SIEMENS_PER_METER = 174, + UNITS_TESLAS = 175, + UNITS_VOLTS = 5, + UNITS_MILLIVOLTS = 124, + UNITS_KILOVOLTS = 6, + UNITS_MEGAVOLTS = 7, + UNITS_VOLT_AMPERES = 8, + UNITS_KILOVOLT_AMPERES = 9, + UNITS_MEGAVOLT_AMPERES = 10, + UNITS_VOLT_AMPERES_REACTIVE = 11, + UNITS_KILOVOLT_AMPERES_REACTIVE = 12, + UNITS_MEGAVOLT_AMPERES_REACTIVE = 13, + UNITS_VOLTS_PER_DEGREE_KELVIN = 176, + UNITS_VOLTS_PER_METER = 177, + UNITS_DEGREES_PHASE = 14, + UNITS_POWER_FACTOR = 15, + UNITS_WEBERS = 178, + /* Energy */ + UNITS_JOULES = 16, + UNITS_KILOJOULES = 17, + UNITS_KILOJOULES_PER_KILOGRAM = 125, + UNITS_MEGAJOULES = 126, + UNITS_WATT_HOURS = 18, + UNITS_KILOWATT_HOURS = 19, + UNITS_MEGAWATT_HOURS = 146, + UNITS_BTUS = 20, + UNITS_KILO_BTUS = 147, + UNITS_MEGA_BTUS = 148, + UNITS_THERMS = 21, + UNITS_TON_HOURS = 22, + /* Enthalpy */ + UNITS_JOULES_PER_KILOGRAM_DRY_AIR = 23, + UNITS_KILOJOULES_PER_KILOGRAM_DRY_AIR = 149, + UNITS_MEGAJOULES_PER_KILOGRAM_DRY_AIR = 150, + UNITS_BTUS_PER_POUND_DRY_AIR = 24, + UNITS_BTUS_PER_POUND = 117, + /* Entropy */ + UNITS_JOULES_PER_DEGREE_KELVIN = 127, + UNITS_KILOJOULES_PER_DEGREE_KELVIN = 151, + UNITS_MEGAJOULES_PER_DEGREE_KELVIN = 152, + UNITS_JOULES_PER_KILOGRAM_DEGREE_KELVIN = 128, + /* Force */ + UNITS_NEWTON = 153, + /* Frequency */ + UNITS_CYCLES_PER_HOUR = 25, + UNITS_CYCLES_PER_MINUTE = 26, + UNITS_HERTZ = 27, + UNITS_KILOHERTZ = 129, + UNITS_MEGAHERTZ = 130, + UNITS_PER_HOUR = 131, + /* Humidity */ + UNITS_GRAMS_OF_WATER_PER_KILOGRAM_DRY_AIR = 28, + UNITS_PERCENT_RELATIVE_HUMIDITY = 29, + /* Length */ + UNITS_MILLIMETERS = 30, + UNITS_CENTIMETERS = 118, + UNITS_METERS = 31, + UNITS_INCHES = 32, + UNITS_FEET = 33, + /* Light */ + UNITS_CANDELAS = 179, + UNITS_CANDELAS_PER_SQUARE_METER = 180, + UNITS_WATTS_PER_SQUARE_FOOT = 34, + UNITS_WATTS_PER_SQUARE_METER = 35, + UNITS_LUMENS = 36, + UNITS_LUXES = 37, + UNITS_FOOT_CANDLES = 38, + /* Mass */ + UNITS_KILOGRAMS = 39, + UNITS_POUNDS_MASS = 40, + UNITS_TONS = 41, + /* Mass Flow */ + UNITS_GRAMS_PER_SECOND = 154, + UNITS_GRAMS_PER_MINUTE = 155, + UNITS_KILOGRAMS_PER_SECOND = 42, + UNITS_KILOGRAMS_PER_MINUTE = 43, + UNITS_KILOGRAMS_PER_HOUR = 44, + UNITS_POUNDS_MASS_PER_SECOND = 119, + UNITS_POUNDS_MASS_PER_MINUTE = 45, + UNITS_POUNDS_MASS_PER_HOUR = 46, + UNITS_TONS_PER_HOUR = 156, + /* Power */ + UNITS_MILLIWATTS = 132, + UNITS_WATTS = 47, + UNITS_KILOWATTS = 48, + UNITS_MEGAWATTS = 49, + UNITS_BTUS_PER_HOUR = 50, + UNITS_KILO_BTUS_PER_HOUR = 157, + UNITS_HORSEPOWER = 51, + UNITS_TONS_REFRIGERATION = 52, + /* Pressure */ + UNITS_PASCALS = 53, + UNITS_HECTOPASCALS = 133, + UNITS_KILOPASCALS = 54, + UNITS_MILLIBARS = 134, + UNITS_BARS = 55, + UNITS_POUNDS_FORCE_PER_SQUARE_INCH = 56, + UNITS_CENTIMETERS_OF_WATER = 57, + UNITS_INCHES_OF_WATER = 58, + UNITS_MILLIMETERS_OF_MERCURY = 59, + UNITS_CENTIMETERS_OF_MERCURY = 60, + UNITS_INCHES_OF_MERCURY = 61, + /* Temperature */ + UNITS_DEGREES_CELSIUS = 62, + UNITS_DEGREES_KELVIN = 63, + UNITS_DEGREES_KELVIN_PER_HOUR = 181, + UNITS_DEGREES_KELVIN_PER_MINUTE = 182, + UNITS_DEGREES_FAHRENHEIT = 64, + UNITS_DEGREE_DAYS_CELSIUS = 65, + UNITS_DEGREE_DAYS_FAHRENHEIT = 66, + UNITS_DELTA_DEGREES_FAHRENHEIT = 120, + UNITS_DELTA_DEGREES_KELVIN = 121, + /* Time */ + UNITS_YEARS = 67, + UNITS_MONTHS = 68, + UNITS_WEEKS = 69, + UNITS_DAYS = 70, + UNITS_HOURS = 71, + UNITS_MINUTES = 72, + UNITS_SECONDS = 73, + UNITS_HUNDREDTHS_SECONDS = 158, + UNITS_MILLISECONDS = 159, + /* Torque */ + UNITS_NEWTON_METERS = 160, + /* Velocity */ + UNITS_MILLIMETERS_PER_SECOND = 161, + UNITS_MILLIMETERS_PER_MINUTE = 162, + UNITS_METERS_PER_SECOND = 74, + UNITS_METERS_PER_MINUTE = 163, + UNITS_METERS_PER_HOUR = 164, + UNITS_KILOMETERS_PER_HOUR = 75, + UNITS_FEET_PER_SECOND = 76, + UNITS_FEET_PER_MINUTE = 77, + UNITS_MILES_PER_HOUR = 78, + /* Volume */ + UNITS_CUBIC_FEET = 79, + UNITS_CUBIC_METERS = 80, + UNITS_IMPERIAL_GALLONS = 81, + UNITS_LITERS = 82, + UNITS_US_GALLONS = 83, + /* Volumetric Flow */ + UNITS_CUBIC_FEET_PER_SECOND = 142, + UNITS_CUBIC_FEET_PER_MINUTE = 84, + UNITS_CUBIC_METERS_PER_SECOND = 85, + UNITS_CUBIC_METERS_PER_MINUTE = 165, + UNITS_CUBIC_METERS_PER_HOUR = 135, + UNITS_IMPERIAL_GALLONS_PER_MINUTE = 86, + UNITS_LITERS_PER_SECOND = 87, + UNITS_LITERS_PER_MINUTE = 88, + UNITS_LITERS_PER_HOUR = 136, + UNITS_US_GALLONS_PER_MINUTE = 89, + /* Other */ + UNITS_DEGREES_ANGULAR = 90, + UNITS_DEGREES_CELSIUS_PER_HOUR = 91, + UNITS_DEGREES_CELSIUS_PER_MINUTE = 92, + UNITS_DEGREES_FAHRENHEIT_PER_HOUR = 93, + UNITS_DEGREES_FAHRENHEIT_PER_MINUTE = 94, + UNITS_JOULE_SECONDS = 183, + UNITS_KILOGRAMS_PER_CUBIC_METER = 186, + UNITS_KILOWATT_HOURS_PER_SQUARE_METER = 137, + UNITS_KILOWATT_HOURS_PER_SQUARE_FOOT = 138, + UNITS_MEGAJOULES_PER_SQUARE_METER = 139, + UNITS_MEGAJOULES_PER_SQUARE_FOOT = 140, + UNITS_NO_UNITS = 95, + UNITS_NEWTON_SECONDS = 187, + UNITS_NEWTONS_PER_METER = 188, + UNITS_PARTS_PER_MILLION = 96, + UNITS_PARTS_PER_BILLION = 97, + UNITS_PERCENT = 98, + UNITS_PERCENT_OBSCURATION_PER_FOOT = 143, + UNITS_PERCENT_OBSCURATION_PER_METER = 144, + UNITS_PERCENT_PER_SECOND = 99, + UNITS_PER_MINUTE = 100, + UNITS_PER_SECOND = 101, + UNITS_PSI_PER_DEGREE_FAHRENHEIT = 102, + UNITS_RADIANS = 103, + UNITS_RADIANS_PER_SECOND = 184, + UNITS_REVOLUTIONS_PER_MINUTE = 104, + UNITS_SQUARE_METERS_PER_NEWTON = 185, + UNITS_WATTS_PER_METER_PER_DEGREE_KELVIN = 189, + UNITS_WATTS_PER_SQUARE_METER_DEGREE_KELVIN = 141 + /* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23. + The last enumeration used in this version is 189. */ +} BACNET_ENGINEERING_UNITS; + +typedef enum { + POLARITY_NORMAL = 0, + POLARITY_REVERSE = 1 +} BACNET_POLARITY; + +typedef enum { + PROGRAM_REQUEST_READY = 0, + PROGRAM_REQUEST_LOAD = 1, + PROGRAM_REQUEST_RUN = 2, + PROGRAM_REQUEST_HALT = 3, + PROGRAM_REQUEST_RESTART = 4, + PROGRAM_REQUEST_UNLOAD = 5 +} BACNET_PROGRAM_REQUEST; + +typedef enum { + PROGRAM_STATE_IDLE = 0, + PROGRAM_STATE_LOADING = 1, + PROGRAM_STATE_RUNNING = 2, + PROGRAM_STATE_WAITING = 3, + PROGRAM_STATE_HALTED = 4, + PROGRAM_STATE_UNLOADING = 5 +} BACNET_PROGRAM_STATE; + +typedef enum { + PROGRAM_ERROR_NORMAL = 0, + PROGRAM_ERROR_LOAD_FAILED = 1, + PROGRAM_ERROR_INTERNAL = 2, + PROGRAM_ERROR_PROGRAM = 3, + PROGRAM_ERROR_OTHER = 4 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ +} BACNET_PROGRAM_ERROR; + +typedef enum { + RELIABILITY_NO_FAULT_DETECTED = 0, + RELIABILITY_NO_SENSOR = 1, + RELIABILITY_OVER_RANGE = 2, + RELIABILITY_UNDER_RANGE = 3, + RELIABILITY_OPEN_LOOP = 4, + RELIABILITY_SHORTED_LOOP = 5, + RELIABILITY_NO_OUTPUT = 6, + RELIABILITY_UNRELIABLE_OTHER = 7, + RELIABILITY_PROCESS_ERROR = 8, + RELIABILITY_MULTI_STATE_FAULT = 9, + RELIABILITY_CONFIGURATION_ERROR = 10 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ +} BACNET_RELIABILITY; + +typedef enum { + EVENT_CHANGE_OF_BITSTRING = 0, + EVENT_CHANGE_OF_STATE = 1, + EVENT_CHANGE_OF_VALUE = 2, + EVENT_COMMAND_FAILURE = 3, + EVENT_FLOATING_LIMIT = 4, + EVENT_OUT_OF_RANGE = 5, + /* complex-event-type (6), -- see comment below */ + /* event-buffer-ready (7), -- context tag 7 is deprecated */ + EVENT_CHANGE_OF_LIFE_SAFETY = 8, + EVENT_EXTENDED = 9, + EVENT_BUFFER_READY = 10, + EVENT_UNSIGNED_RANGE = 11 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* It is expected that these enumerated values will correspond to */ + /* the use of the complex-event-type CHOICE [6] of the */ + /* BACnetNotificationParameters production. */ + /* The last enumeration used in this version is 11. */ +} BACNET_EVENT_TYPE; + +typedef enum { + FILE_RECORD_ACCESS = 0, + FILE_STREAM_ACCESS = 1, + FILE_RECORD_AND_STREAM_ACCESS = 2 +} BACNET_FILE_ACCESS_METHOD; + +typedef enum { + MIN_LIFE_SAFETY_MODE = 0, + LIFE_SAFETY_MODE_OFF = 0, + LIFE_SAFETY_MODE_ON = 1, + LIFE_SAFETY_MODE_TEST = 2, + LIFE_SAFETY_MODE_MANNED = 3, + LIFE_SAFETY_MODE_UNMANNED = 4, + LIFE_SAFETY_MODE_ARMED = 5, + LIFE_SAFETY_MODE_DISARMED = 6, + LIFE_SAFETY_MODE_PREARMED = 7, + LIFE_SAFETY_MODE_SLOW = 8, + LIFE_SAFETY_MODE_FAST = 9, + LIFE_SAFETY_MODE_DISCONNECTED = 10, + LIFE_SAFETY_MODE_ENABLED = 11, + LIFE_SAFETY_MODE_DISABLED = 12, + LIFE_SAFETY_MODE_AUTOMATIC_RELEASE_DISABLED = 13, + LIFE_SAFETY_MODE_DEFAULT = 14, + MAX_LIFE_SAFETY_MODE = 14 + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ +} BACNET_LIFE_SAFETY_MODE; + +typedef enum { + LIFE_SAFETY_OPERATION_NONE = 0, + LIFE_SAFETY_OPERATION_SILENCE = 1, + LIFE_SAFETY_OPERATION_SILENCE_AUDIBLE = 2, + LIFE_SAFETY_OPERATION_SILENCE_VISUAL = 3, + LIFE_SAFETY_OPERATION_RESET = 4, + LIFE_SAFETY_OPERATION_RESET_ALARM = 5, + LIFE_SAFETY_OPERATION_RESET_FAULT = 6, + LIFE_SAFETY_OPERATION_UNSILENCE = 7, + LIFE_SAFETY_OPERATION_UNSILENCE_AUDIBLE = 8, + LIFE_SAFETY_OPERATION_UNSILENCE_VISUAL = 9 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ +} BACNET_LIFE_SAFETY_OPERATION; + +typedef enum { + MIN_LIFE_SAFETY_STATE = 0, + LIFE_SAFETY_STATE_QUIET = 0, + LIFE_SAFETY_STATE_PRE_ALARM = 1, + LIFE_SAFETY_STATE_ALARM = 2, + LIFE_SAFETY_STATE_FAULT = 3, + LIFE_SAFETY_STATE_FAULT_PRE_ALARM = 4, + LIFE_SAFETY_STATE_FAULT_ALARM = 5, + LIFE_SAFETY_STATE_NOT_READY = 6, + LIFE_SAFETY_STATE_ACTIVE = 7, + LIFE_SAFETY_STATE_TAMPER = 8, + LIFE_SAFETY_STATE_TEST_ALARM = 9, + LIFE_SAFETY_STATE_TEST_ACTIVE = 10, + LIFE_SAFETY_STATE_TEST_FAULT = 11, + LIFE_SAFETY_STATE_TEST_FAULT_ALARM = 12, + LIFE_SAFETY_STATE_HOLDUP = 13, + LIFE_SAFETY_STATE_DURESS = 14, + LIFE_SAFETY_STATE_TAMPER_ALARM = 15, + LIFE_SAFETY_STATE_ABNORMAL = 16, + LIFE_SAFETY_STATE_EMERGENCY_POWER = 17, + LIFE_SAFETY_STATE_DELAYED = 18, + LIFE_SAFETY_STATE_BLOCKED = 19, + LIFE_SAFETY_STATE_LOCAL_ALARM = 20, + LIFE_SAFETY_STATE_GENERAL_ALARM = 21, + LIFE_SAFETY_STATE_SUPERVISORY = 22, + LIFE_SAFETY_STATE_TEST_SUPERVISORY = 23, + MAX_LIFE_SAFETY_STATE = 0 + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ +} BACNET_LIFE_SAFETY_STATE; + +typedef enum { + SILENCED_STATE_UNSILENCED = 0, + SILENCED_STATE_AUDIBLE_SILENCED = 1, + SILENCED_STATE_VISIBLE_SILENCED = 2, + SILENCED_STATE_ALL_SILENCED = 3 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ +} BACNET_SILENCED_STATE; + +typedef enum { + MAINTENANCE_NONE = 0, + MAINTENANCE_PERIODIC_TEST = 1, + AINTENANCE_NEED_SERVICE_OPERATIONAL = 2, + MAINTENANCE_NEED_SERVICE_INOPERATIVE = 3 + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ +} BACNET_MAINTENANCE; + +typedef enum { + NOTIFY_ALARM = 0, + NOTIFY_EVENT = 1, + NOTIFY_ACK_NOTIFICATION = 2 +} BACNET_NOTIFY_TYPE; + +typedef enum { + OBJECT_ANALOG_INPUT = 0, + OBJECT_ANALOG_OUTPUT = 1, + OBJECT_ANALOG_VALUE = 2, + OBJECT_BINARY_INPUT = 3, + OBJECT_BINARY_OUTPUT = 4, + OBJECT_BINARY_VALUE = 5, + OBJECT_CALENDAR = 6, + OBJECT_COMMAND = 7, + OBJECT_DEVICE = 8, + OBJECT_EVENT_ENROLLMENT = 9, + OBJECT_FILE = 10, + OBJECT_GROUP = 11, + OBJECT_LOOP = 12, + OBJECT_MULTI_STATE_INPUT = 13, + OBJECT_MULTI_STATE_OUTPUT = 14, + OBJECT_NOTIFICATION_CLASS = 15, + OBJECT_PROGRAM = 16, + OBJECT_SCHEDULE = 17, + OBJECT_AVERAGING = 18, + OBJECT_MULTI_STATE_VALUE = 19, + OBJECT_TRENDLOG = 20, + OBJECT_LIFE_SAFETY_POINT = 21, + OBJECT_LIFE_SAFETY_ZONE = 22, + OBJECT_ACCUMULATOR = 23, + OBJECT_PULSE_CONVERTER = 24, + OBJECT_EVENT_LOG = 25, + OBJECT_GLOBAL_GROUP = 26, + OBJECT_TREND_LOG_MULTIPLE = 27, + OBJECT_LOAD_CONTROL = 28, + OBJECT_STRUCTURED_VIEW = 29, + /* Enumerated values 0-127 are reserved for definition by ASHRAE. */ + /* Enumerated values 128-1023 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_ASHRAE_OBJECT_TYPE = 30, /* used for bit string loop */ + MAX_BACNET_OBJECT_TYPE = 1023 +} BACNET_OBJECT_TYPE; + +typedef enum { + SEGMENTATION_BOTH = 0, + SEGMENTATION_TRANSMIT = 1, + SEGMENTATION_RECEIVE = 2, + SEGMENTATION_NONE = 3, + MAX_BACNET_SEGMENTATION = 4 +} BACNET_SEGMENTATION; + +typedef enum { + VT_CLASS_DEFAULT = 0, + VT_CLASS_ANSI_X34 = 1, /* real name is ANSI X3.64 */ + VT_CLASS_DEC_VT52 = 2, + VT_CLASS_DEC_VT100 = 3, + VT_CLASS_DEC_VT220 = 4, + VT_CLASS_HP_700_94 = 5, /* real name is HP 700/94 */ + VT_CLASS_IBM_3130 = 6 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ +} BACNET_VT_CLASS; + +typedef enum { + CHARACTER_ANSI_X34 = 0, + CHARACTER_MS_DBCS = 1, + CHARACTER_JISC_6226 = 2, + CHARACTER_UCS4 = 3, + CHARACTER_UCS2 = 4, + CHARACTER_ISO8859 = 5 +} BACNET_CHARACTER_STRING_ENCODING; + +typedef enum { + BACNET_APPLICATION_TAG_NULL = 0, + BACNET_APPLICATION_TAG_BOOLEAN = 1, + BACNET_APPLICATION_TAG_UNSIGNED_INT = 2, + BACNET_APPLICATION_TAG_SIGNED_INT = 3, + BACNET_APPLICATION_TAG_REAL = 4, + BACNET_APPLICATION_TAG_DOUBLE = 5, + BACNET_APPLICATION_TAG_OCTET_STRING = 6, + BACNET_APPLICATION_TAG_CHARACTER_STRING = 7, + BACNET_APPLICATION_TAG_BIT_STRING = 8, + BACNET_APPLICATION_TAG_ENUMERATED = 9, + BACNET_APPLICATION_TAG_DATE = 10, + BACNET_APPLICATION_TAG_TIME = 11, + BACNET_APPLICATION_TAG_OBJECT_ID = 12, + BACNET_APPLICATION_TAG_RESERVED1 = 13, + BACNET_APPLICATION_TAG_RESERVED2 = 14, + BACNET_APPLICATION_TAG_RESERVED3 = 15, + MAX_BACNET_APPLICATION_TAG = 16 +} BACNET_APPLICATION_TAG; + +/* note: these are not the real values, */ +/* but are shifted left for easy encoding */ +typedef enum { + PDU_TYPE_CONFIRMED_SERVICE_REQUEST = 0, + PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST = 0x10, + PDU_TYPE_SIMPLE_ACK = 0x20, + PDU_TYPE_COMPLEX_ACK = 0x30, + PDU_TYPE_SEGMENT_ACK = 0x40, + PDU_TYPE_ERROR = 0x50, + PDU_TYPE_REJECT = 0x60, + PDU_TYPE_ABORT = 0x70 +} BACNET_PDU_TYPE; + +typedef enum { + /* Alarm and Event Services */ + SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM = 0, + SERVICE_CONFIRMED_COV_NOTIFICATION = 1, + SERVICE_CONFIRMED_EVENT_NOTIFICATION = 2, + SERVICE_CONFIRMED_GET_ALARM_SUMMARY = 3, + SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY = 4, + SERVICE_CONFIRMED_GET_EVENT_INFORMATION = 29, + SERVICE_CONFIRMED_SUBSCRIBE_COV = 5, + SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY = 28, + SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION = 27, + /* File Access Services */ + SERVICE_CONFIRMED_ATOMIC_READ_FILE = 6, + SERVICE_CONFIRMED_ATOMIC_WRITE_FILE = 7, + /* Object Access Services */ + SERVICE_CONFIRMED_ADD_LIST_ELEMENT = 8, + SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT = 9, + SERVICE_CONFIRMED_CREATE_OBJECT = 10, + SERVICE_CONFIRMED_DELETE_OBJECT = 11, + SERVICE_CONFIRMED_READ_PROPERTY = 12, + SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL = 13, + SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE = 14, + SERVICE_CONFIRMED_READ_RANGE = 26, + SERVICE_CONFIRMED_WRITE_PROPERTY = 15, + SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE = 16, + /* Remote Device Management Services */ + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL = 17, + SERVICE_CONFIRMED_PRIVATE_TRANSFER = 18, + SERVICE_CONFIRMED_TEXT_MESSAGE = 19, + SERVICE_CONFIRMED_REINITIALIZE_DEVICE = 20, + /* Virtual Terminal Services */ + SERVICE_CONFIRMED_VT_OPEN = 21, + SERVICE_CONFIRMED_VT_CLOSE = 22, + SERVICE_CONFIRMED_VT_DATA = 23, + /* Security Services */ + SERVICE_CONFIRMED_AUTHENTICATE = 24, + SERVICE_CONFIRMED_REQUEST_KEY = 25, + /* Services added after 1995 */ + /* readRange (26) see Object Access Services */ + /* lifeSafetyOperation (27) see Alarm and Event Services */ + /* subscribeCOVProperty (28) see Alarm and Event Services */ + /* getEventInformation (29) see Alarm and Event Services */ + MAX_BACNET_CONFIRMED_SERVICE = 30 +} BACNET_CONFIRMED_SERVICE; + +typedef enum { + SERVICE_UNCONFIRMED_I_AM = 0, + SERVICE_UNCONFIRMED_I_HAVE = 1, + SERVICE_UNCONFIRMED_COV_NOTIFICATION = 2, + SERVICE_UNCONFIRMED_EVENT_NOTIFICATION = 3, + SERVICE_UNCONFIRMED_PRIVATE_TRANSFER = 4, + SERVICE_UNCONFIRMED_TEXT_MESSAGE = 5, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION = 6, + SERVICE_UNCONFIRMED_WHO_HAS = 7, + SERVICE_UNCONFIRMED_WHO_IS = 8, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9, + /* Other services to be added as they are defined. */ + /* All choice values in this production are reserved */ + /* for definition by ASHRAE. */ + /* Proprietary extensions are made by using the */ + /* UnconfirmedPrivateTransfer service. See Clause 23. */ + MAX_BACNET_UNCONFIRMED_SERVICE = 10 +} BACNET_UNCONFIRMED_SERVICE; + +/* Bit String Enumerations */ +typedef enum { + /* Alarm and Event Services */ + SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM = 0, + SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION = 1, + SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION = 2, + SERVICE_SUPPORTED_GET_ALARM_SUMMARY = 3, + SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY = 4, + SERVICE_SUPPORTED_GET_EVENT_INFORMATION = 39, + SERVICE_SUPPORTED_SUBSCRIBE_COV = 5, + SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY = 38, + SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION = 37, + /* File Access Services */ + SERVICE_SUPPORTED_ATOMIC_READ_FILE = 6, + SERVICE_SUPPORTED_ATOMIC_WRITE_FILE = 7, + /* Object Access Services */ + SERVICE_SUPPORTED_ADD_LIST_ELEMENT = 8, + SERVICE_SUPPORTED_REMOVE_LIST_ELEMENT = 9, + SERVICE_SUPPORTED_CREATE_OBJECT = 10, + SERVICE_SUPPORTED_DELETE_OBJECT = 11, + SERVICE_SUPPORTED_READ_PROPERTY = 12, + SERVICE_SUPPORTED_READ_PROPERTY_CONDITIONAL = 13, + SERVICE_SUPPORTED_READ_PROPERTY_MULTIPLE = 14, + SERVICE_SUPPORTED_READ_RANGE = 35, + SERVICE_SUPPORTED_WRITE_PROPERTY = 15, + SERVICE_SUPPORTED_WRITE_PROPERTY_MULTIPLE = 16, + /* Remote Device Management Services */ + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL = 17, + SERVICE_SUPPORTED_PRIVATE_TRANSFER = 18, + SERVICE_SUPPORTED_TEXT_MESSAGE = 19, + SERVICE_SUPPORTED_REINITIALIZE_DEVICE = 20, + /* Virtual Terminal Services */ + SERVICE_SUPPORTED_VT_OPEN = 21, + SERVICE_SUPPORTED_VT_CLOSE = 22, + SERVICE_SUPPORTED_VT_DATA = 23, + /* Security Services */ + SERVICE_SUPPORTED_AUTHENTICATE = 24, + SERVICE_SUPPORTED_REQUEST_KEY = 25, + SERVICE_SUPPORTED_I_AM = 26, + SERVICE_SUPPORTED_I_HAVE = 27, + SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION = 28, + SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION = 29, + SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER = 30, + SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE = 31, + SERVICE_SUPPORTED_TIME_SYNCHRONIZATION = 32, + SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION = 36, + SERVICE_SUPPORTED_WHO_HAS = 33, + SERVICE_SUPPORTED_WHO_IS = 34, + /* Other services to be added as they are defined. */ + /* All values in this production are reserved */ + /* for definition by ASHRAE. */ + MAX_BACNET_SERVICES_SUPPORTED = 40 +} BACNET_SERVICES_SUPPORTED; + +typedef enum { + BVLC_RESULT = 0, + BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE = 1, + BVLC_READ_BROADCAST_DISTRIBUTION_TABLE = 2, + BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK = 3, + BVLC_FORWARDED_NPDU = 4, + BVLC_REGISTER_FOREIGN_DEVICE = 5, + BVLC_READ_FOREIGN_DEVICE_TABLE = 6, + BVLC_READ_FOREIGN_DEVICE_TABLE_ACK = 7, + BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY = 8, + BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK = 9, + BVLC_ORIGINAL_UNICAST_NPDU = 10, + BVLC_ORIGINAL_BROADCAST_NPDU = 11, + MAX_BVLC_FUNCTION = 12 +} BACNET_BVLC_FUNCTION; + +typedef enum { + BVLC_RESULT_SUCCESSFUL_COMPLETION = 0x0000, + BVLC_RESULT_WRITE_BROADCAST_DISTRIBUTION_TABLE_NAK = 0x0010, + BVLC_RESULT_READ_BROADCAST_DISTRIBUTION_TABLE_NAK = 0x0020, + BVLC_RESULT_REGISTER_FOREIGN_DEVICE_NAK = 0X0030, + BVLC_RESULT_READ_FOREIGN_DEVICE_TABLE_NAK = 0x0040, + BVLC_RESULT_DELETE_FOREIGN_DEVICE_TABLE_ENTRY_NAK = 0x0050, + BVLC_RESULT_DISTRIBUTE_BROADCAST_TO_NETWORK_NAK = 0x0060 +} BACNET_BVLC_RESULT; + +/* Bit String Enumerations */ +typedef enum { + STATUS_FLAG_IN_ALARM = 0, + STATUS_FLAG_FAULT = 1, + STATUS_FLAG_OVERRIDDEN = 2, + STATUS_FLAG_OUT_OF_SERVICE = 3 +} BACNET_STATUS_FLAGS; + +typedef enum { + ACKNOWLEDGMENT_FILTER_ALL = 0, + ACKNOWLEDGMENT_FILTER_ACKED = 1, + ACKNOWLEDGMENT_FILTER_NOT_ACKED = 2 +} BACNET_ACKNOWLEDGMENT_FILTER; + +typedef enum { + EVENT_STATE_FILTER_OFFNORMAL = 0, + EVENT_STATE_FILTER_FAULT = 1, + EVENT_STATE_FILTER_NORMAL = 2, + EVENT_STATE_FILTER_ALL = 3, + EVENT_STATE_FILTER_ACTIVE = 4 +} BACNET_EVENT_STATE_FILTER; + +typedef enum { + SELECTION_LOGIC_AND = 0, + SELECTION_LOGIC_OR = 1, + SELECTION_LOGIC_ALL = 2 +} BACNET_SELECTION_LOGIC; + +typedef enum { + RELATION_SPECIFIER_EQUAL = 0, + RELATION_SPECIFIER_NOT_EQUAL = 1, + RELATION_SPECIFIER_LESS_THAN = 2, + RELATION_SPECIFIER_GREATER_THAN = 3, + RELATION_SPECIFIER_LESS_THAN_OR_EQUAL = 4, + RELATION_SPECIFIER_GREATER_THAN_OR_EQUAL = 5 +} BACNET_RELATION_SPECIFIER; + +typedef enum { + COMMUNICATION_ENABLE = 0, + COMMUNICATION_DISABLE = 1, + COMMUNICATION_DISABLE_INITIATION = 2, + MAX_BACNET_COMMUNICATION_ENABLE_DISABLE = 3 +} BACNET_COMMUNICATION_ENABLE_DISABLE; + +typedef enum { + MESSAGE_PRIORITY_NORMAL = 0, + MESSAGE_PRIORITY_URGENT = 1, + MESSAGE_PRIORITY_CRITICAL_EQUIPMENT = 2, + MESSAGE_PRIORITY_LIFE_SAFETY = 3 +} BACNET_MESSAGE_PRIORITY; + +/*Network Layer Message Type */ +/*If Bit 7 of the control octet described in 6.2.2 is 1, */ +/* a message type octet shall be present as shown in Figure 6-1. */ +/* The following message types are indicated: */ +typedef enum { + NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK = 0, + NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK = 1, + NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK = 2, + NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK = 3, + NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK = 4, + NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK = 5, + NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE = 6, + NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE_ACK = 7, + NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK = 8, + NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK = 9 + /* X'0A' to X'7F': Reserved for use by ASHRAE, */ + /* X'80' to X'FF': Available for vendor proprietary messages */ +} BACNET_NETWORK_MESSAGE_TYPE; + + +typedef enum { + REINITIALIZED_STATE_COLD_START = 0, + REINITIALIZED_STATE_WARM_START = 1, + REINITIALIZED_STATE_START_BACKUP = 2, + REINITIALIZED_STATE_END_BACKUP = 3, + REINITIALIZED_STATE_START_RESTORE = 4, + REINITIALIZED_STATE_END_RESTORE = 5, + REINITIALIZED_STATE_ABORT_RESTORE = 6, + REINITIALIZED_STATE_IDLE = 255 +} BACNET_REINITIALIZED_STATE_OF_DEVICE; + +typedef enum { + ABORT_REASON_OTHER = 0, + ABORT_REASON_BUFFER_OVERFLOW = 1, + ABORT_REASON_INVALID_APDU_IN_THIS_STATE = 2, + ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK = 3, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED = 4, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_ABORT_REASON = 5, + FIRST_PROPRIETARY_ABORT_REASON = 64, + LAST_PROPRIETARY_ABORT_REASON = 65535 +} BACNET_ABORT_REASON; + +typedef enum { + REJECT_REASON_OTHER = 0, + REJECT_REASON_BUFFER_OVERFLOW = 1, + REJECT_REASON_INCONSISTENT_PARAMETERS = 2, + REJECT_REASON_INVALID_PARAMETER_DATA_TYPE = 3, + REJECT_REASON_INVALID_TAG = 4, + REJECT_REASON_MISSING_REQUIRED_PARAMETER = 5, + REJECT_REASON_PARAMETER_OUT_OF_RANGE = 6, + REJECT_REASON_TOO_MANY_ARGUMENTS = 7, + REJECT_REASON_UNDEFINED_ENUMERATION = 8, + REJECT_REASON_UNRECOGNIZED_SERVICE = 9, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_REJECT_REASON = 10, + FIRST_PROPRIETARY_REJECT_REASON = 64, + LAST_PROPRIETARY_REJECT_REASON = 65535 +} BACNET_BACNET_REJECT_REASON; + +typedef enum { + ERROR_CLASS_DEVICE = 0, + ERROR_CLASS_OBJECT = 1, + ERROR_CLASS_PROPERTY = 2, + ERROR_CLASS_RESOURCES = 3, + ERROR_CLASS_SECURITY = 4, + ERROR_CLASS_SERVICES = 5, + ERROR_CLASS_VT = 6, + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + MAX_BACNET_ERROR_CLASS = 7, + FIRST_PROPRIETARY_ERROR_CLASS = 64, + LAST_PROPRIETARY_ERROR_CLASS = 65535 +} BACNET_ERROR_CLASS; + +/* These are sorted in the order given in + Clause 18. ERROR, REJECT AND ABORT CODES + The Class and Code pairings are required + to be used in accordance with Clause 18. */ +typedef enum { + /* valid for all classes */ + ERROR_CODE_OTHER = 0, + + /* Error Class - Device */ + ERROR_CODE_DEVICE_BUSY = 3, + ERROR_CODE_CONFIGURATION_IN_PROGRESS = 2, + ERROR_CODE_OPERATIONAL_PROBLEM = 25, + + /* Error Class - Object */ + ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED = 4, + ERROR_CODE_NO_OBJECTS_OF_SPECIFIED_TYPE = 17, + ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED = 23, + ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS = 24, + ERROR_CODE_READ_ACCESS_DENIED = 27, + ERROR_CODE_UNKNOWN_OBJECT = 31, + ERROR_CODE_UNSUPPORTED_OBJECT_TYPE = 36, + + /* Error Class - Property */ + ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, + ERROR_CODE_DATATYPE_NOT_SUPPORTED = 47, + ERROR_CODE_INCONSISTENT_SELECTION_CRITERION = 8, + ERROR_CODE_INVALID_ARRAY_INDEX = 42, + ERROR_CODE_INVALID_DATA_TYPE = 9, + ERROR_CODE_NOT_COV_PROPERTY = 44, + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED = 45, + ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY = 50, + /* ERROR_CODE_READ_ACCESS_DENIED = 27, */ + ERROR_CODE_UNKNOWN_PROPERTY = 32, + ERROR_CODE_VALUE_OUT_OF_RANGE = 37, + ERROR_CODE_WRITE_ACCESS_DENIED = 40, + + /* Error Class - Resources */ + ERROR_CODE_NO_SPACE_FOR_OBJECT = 18, + ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT = 19, + ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY = 20, + + /* Error Class - Security */ + ERROR_CODE_AUTHENTICATION_FAILED = 1, + /* ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, */ + ERROR_CODE_INCOMPATIBLE_SECURITY_LEVELS = 6, + ERROR_CODE_INVALID_OPERATOR_NAME = 12, + ERROR_CODE_KEY_GENERATION_ERROR = 15, + ERROR_CODE_PASSWORD_FAILURE = 26, + ERROR_CODE_SECURITY_NOT_SUPPORTED = 28, + ERROR_CODE_TIMEOUT = 30, + + /* Error Class - Services */ + /* ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED = 41, */ + ERROR_CODE_COV_SUBSCRIPTION_FAILED = 43, + ERROR_CODE_DUPLICATE_NAME = 48, + ERROR_CODE_DUPLICATE_OBJECT_ID = 49, + ERROR_CODE_FILE_ACCESS_DENIED = 5, + ERROR_CODE_INCONSISTENT_PARAMETERS = 7, + ERROR_CODE_INVALID_CONFIGURATION_DATA = 46, + ERROR_CODE_INVALID_FILE_ACCESS_METHOD = 10, + ERROR_CODE_ERROR_CODE_INVALID_FILE_START_POSITION = 11, + ERROR_CODE_INVALID_PARAMETER_DATA_TYPE = 13, + ERROR_CODE_INVALID_TIME_STAMP = 14, + ERROR_CODE_MISSING_REQUIRED_PARAMETER = 16, + /* ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED = 45, */ + ERROR_CODE_PROPERTY_IS_NOT_A_LIST = 22, + ERROR_CODE_SERVICE_REQUEST_DENIED = 29, + + /* Error Class - VT */ + ERROR_CODE_UNKNOWN_VT_CLASS = 34, + ERROR_CODE_UNKNOWN_VT_SESSION = 35, + ERROR_CODE_NO_VT_SESSIONS_AVAILABLE = 21, + ERROR_CODE_VT_SESSION_ALREADY_CLOSED = 38, + ERROR_CODE_VT_SESSION_TERMINATION_FAILURE = 39, + + /* unused */ + ERROR_CODE_RESERVED1 = 33, + /* Enumerated values 0-255 are reserved for definition by ASHRAE. */ + /* Enumerated values 256-65535 may be used by others subject to */ + /* the procedures and constraints described in Clause 23. */ + /* The last enumeration used in this version is 50. */ + MAX_BACNET_ERROR_CODE = 51, + FIRST_PROPRIETARY_ERROR_CODE = 256, + LAST_PROPRIETARY_ERROR_CODE = 65535 +} BACNET_ERROR_CODE; + +typedef enum { + BACNET_REINIT_COLDSTART = 0, + BACNET_REINIT_WARMSTART = 1, + BACNET_REINIT_STARTBACKUP = 2, + BACNET_REINIT_ENDBACKUP = 3, + BACNET_REINIT_STARTRESTORE = 4, + BACNET_REINIT_ENDRESTORE = 5, + BACNET_REINIT_ABORTRESTORE = 6, + MAX_BACNET_REINITIALIZED_STATE = 7 +} BACNET_REINITIALIZED_STATE; + +typedef enum BACnetNodeType { + BACNET_NODE_UNKNOWN = 0, + BACNET_NODE_SYSTEM = 1, + BACNET_NODE_NETWORK = 2, + BACNET_NODE_DEVICE = 3, + BACNET_NODE_ORGANIZATIONAL = 4, + BACNET_NODE_AREA = 5, + BACNET_NODE_EQUIPMENT = 6, + BACNET_NODE_POINT = 7, + BACNET_NODE_COLLECTION = 8, + BACNET_NODE_PROPERTY = 9, + BACNET_NODE_FUNCTIONAL = 10, + BACNET_NODE_OTHER = 11 +} BACNET_NODE_TYPE; + +typedef enum BACnetShedState { + BACNET_SHED_INACTIVE = 0, + BACNET_SHED_REQUEST_PENDING = 1, + BACNET_SHED_COMPLIANT = 2, + BACNET_SHED_NON_COMPLIANT = 3 +} BACNET_SHED_STATE; + +#endif /* end of BACENUM_H */ diff --git a/bacnet-stack-0-3-0/bacerror.c b/bacnet-stack-0-3-0/bacerror.c new file mode 100644 index 00000000..2e8110d6 --- /dev/null +++ b/bacnet-stack-0-3-0/bacerror.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/bacerror.h b/bacnet-stack-0-3-0/bacerror.h new file mode 100644 index 00000000..60b3c883 --- /dev/null +++ b/bacnet-stack-0-3-0/bacerror.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/bacerror.mak b/bacnet-stack-0-3-0/bacerror.mak new file mode 100644 index 00000000..a4ef15d0 --- /dev/null +++ b/bacnet-stack-0-3-0/bacerror.mak @@ -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 diff --git a/bacnet-stack-0-3-0/bacprop.c b/bacnet-stack-0-3-0/bacprop.c new file mode 100644 index 00000000..7bfa0013 --- /dev/null +++ b/bacnet-stack-0-3-0/bacprop.c @@ -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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/bacprop.h b/bacnet-stack-0-3-0/bacprop.h new file mode 100644 index 00000000..9fb34dc8 --- /dev/null +++ b/bacnet-stack-0-3-0/bacprop.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/bacstr.c b/bacnet-stack-0-3-0/bacstr.c new file mode 100644 index 00000000..cd142503 --- /dev/null +++ b/bacnet-stack-0-3-0/bacstr.c @@ -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 +#include +#include /* 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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/bacstr.h b/bacnet-stack-0-3-0/bacstr.h new file mode 100644 index 00000000..048d9f4a --- /dev/null +++ b/bacnet-stack-0-3-0/bacstr.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/bacstr.mak b/bacnet-stack-0-3-0/bacstr.mak new file mode 100644 index 00000000..24d82957 --- /dev/null +++ b/bacnet-stack-0-3-0/bacstr.mak @@ -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 diff --git a/bacnet-stack-0-3-0/bactext.c b/bacnet-stack-0-3-0/bactext.c new file mode 100644 index 00000000..46a8f4ff --- /dev/null +++ b/bacnet-stack-0-3-0/bactext.c @@ -0,0 +1,1494 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005-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 +#include "indtext.h" +#include "bacenum.h" + +static const char *ASHRAE_Reserved_String = "Reserved for Use by ASHRAE"; +static const char *Vendor_Proprietary_String = "Vendor Proprietary Value"; + +INDTEXT_DATA bacnet_confirmed_service_names[] = { + {SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM, "Acknowledge-Alarm"}, + {SERVICE_CONFIRMED_COV_NOTIFICATION, "COV-Notification"}, + {SERVICE_CONFIRMED_EVENT_NOTIFICATION, "Event-Notification"}, + {SERVICE_CONFIRMED_GET_ALARM_SUMMARY, "Get-Alarm-Summary"}, + {SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY, "Get-Enrollment-Summary"}, + {SERVICE_CONFIRMED_SUBSCRIBE_COV, "Subscribe-COV"}, + {SERVICE_CONFIRMED_ATOMIC_READ_FILE, "Atomic-Read-File"}, + {SERVICE_CONFIRMED_ATOMIC_WRITE_FILE, "Atomic-Write-File"}, + {SERVICE_CONFIRMED_ADD_LIST_ELEMENT, "Add-List-Element"}, + {SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT, "Remove-List-Element"}, + {SERVICE_CONFIRMED_CREATE_OBJECT, "Create-Object"}, + {SERVICE_CONFIRMED_DELETE_OBJECT, "Delete-Object"}, + {SERVICE_CONFIRMED_READ_PROPERTY, "Read-Property"}, + {SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL, + "Read-Property-Conditional"}, + {SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE, "Read-Property-Multiple"}, + {SERVICE_CONFIRMED_WRITE_PROPERTY, "Write-Property"}, + {SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE, "Write-Property-Multiple"}, + {SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + "Device-Communication-Control"}, + {SERVICE_CONFIRMED_PRIVATE_TRANSFER, "Private-Transfer"}, + {SERVICE_CONFIRMED_TEXT_MESSAGE, "Text-Message"}, + {SERVICE_CONFIRMED_REINITIALIZE_DEVICE, "Reinitialize-Device"}, + {SERVICE_CONFIRMED_VT_OPEN, "VT-Open"}, + {SERVICE_CONFIRMED_VT_CLOSE, "VT-Close"}, + {SERVICE_CONFIRMED_VT_DATA, "VT-Data"}, + {SERVICE_CONFIRMED_AUTHENTICATE, "Authenticate"}, + {SERVICE_CONFIRMED_REQUEST_KEY, "Request-Key"}, + {SERVICE_CONFIRMED_READ_RANGE, "Read-Range"}, + {SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION, "Life-Safety_Operation"}, + {SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY, "Subscribe-COV-Property"}, + {SERVICE_CONFIRMED_GET_EVENT_INFORMATION, "Get-Event-Information"}, + {0, NULL} +}; + +const char *bactext_confirmed_service_name(int index) +{ + return indtext_by_index_default(bacnet_confirmed_service_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_unconfirmed_service_names[] = { + {SERVICE_UNCONFIRMED_I_AM, "I-Am"} + , + {SERVICE_UNCONFIRMED_I_HAVE, "I-Have"} + , + {SERVICE_UNCONFIRMED_COV_NOTIFICATION, "COV-Notification"} + , + {SERVICE_UNCONFIRMED_EVENT_NOTIFICATION, "Event-Notification"} + , + {SERVICE_UNCONFIRMED_PRIVATE_TRANSFER, "Private-Transfer"} + , + {SERVICE_UNCONFIRMED_TEXT_MESSAGE, "Text-Message"} + , + {SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, "Time-Synchronization"} + , + {SERVICE_UNCONFIRMED_WHO_HAS, "Who-Has"} + , + {SERVICE_UNCONFIRMED_WHO_IS, "Who-Is"} + , + {SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + "UTC-Time-Synchronization"} + , + {0, NULL} +}; + +const char *bactext_unconfirmed_service_name(int index) +{ + return indtext_by_index_default(bacnet_unconfirmed_service_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_application_tag_names[] = { + {BACNET_APPLICATION_TAG_NULL, "Null"} + , + {BACNET_APPLICATION_TAG_BOOLEAN, "Boolean"} + , + {BACNET_APPLICATION_TAG_UNSIGNED_INT, "Unsigned Int"} + , + {BACNET_APPLICATION_TAG_SIGNED_INT, "Signed Int"} + , + {BACNET_APPLICATION_TAG_REAL, "Real"} + , + {BACNET_APPLICATION_TAG_DOUBLE, "Double"} + , + {BACNET_APPLICATION_TAG_OCTET_STRING, "Octet String"} + , + {BACNET_APPLICATION_TAG_CHARACTER_STRING, "Character String"} + , + {BACNET_APPLICATION_TAG_BIT_STRING, "Bit String"} + , + {BACNET_APPLICATION_TAG_ENUMERATED, "Enumerated"} + , + {BACNET_APPLICATION_TAG_DATE, "Date"} + , + {BACNET_APPLICATION_TAG_TIME, "Time"} + , + {BACNET_APPLICATION_TAG_OBJECT_ID, "Object ID"} + , + {BACNET_APPLICATION_TAG_RESERVED1, "Reserved 1"} + , + {BACNET_APPLICATION_TAG_RESERVED2, "Reserved 2"} + , + {BACNET_APPLICATION_TAG_RESERVED3, "Reserved 3"} + , + {0, NULL} +}; + +const char *bactext_application_tag_name(int index) +{ + return indtext_by_index_default(bacnet_application_tag_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_object_type_names[] = { + {OBJECT_ANALOG_INPUT, "Analog Input"} + , + {OBJECT_ANALOG_OUTPUT, "Analog Output"} + , + {OBJECT_ANALOG_VALUE, "Analog Value"} + , + {OBJECT_BINARY_INPUT, "Binary Input"} + , + {OBJECT_BINARY_OUTPUT, "Binary Output"} + , + {OBJECT_BINARY_VALUE, "Binary Value"} + , + {OBJECT_CALENDAR, "Calendar"} + , + {OBJECT_COMMAND, "Command"} + , + {OBJECT_DEVICE, "Device"} + , + {OBJECT_EVENT_ENROLLMENT, "Event Enrollment"} + , + {OBJECT_FILE, "File"} + , + {OBJECT_GROUP, "Group"} + , + {OBJECT_LOOP, "Loop"} + , + {OBJECT_MULTI_STATE_INPUT, "Multi-State Input"} + , + {OBJECT_MULTI_STATE_OUTPUT, "Multi-State Output"} + , + {OBJECT_NOTIFICATION_CLASS, "Notification Class"} + , + {OBJECT_PROGRAM, "Program"} + , + {OBJECT_SCHEDULE, "Schedule"} + , + {OBJECT_AVERAGING, "Averaging"} + , + {OBJECT_MULTI_STATE_VALUE, "Multi-State Value"} + , + {OBJECT_TRENDLOG, "Trendlog"} + , + {OBJECT_LIFE_SAFETY_POINT, "Life Safety Point"} + , + {OBJECT_LIFE_SAFETY_ZONE, "Life Safety Zone"} + , + {OBJECT_ACCUMULATOR, "Accumulator"} + , + {OBJECT_PULSE_CONVERTER, "Pulse-Converter"} + , + + {OBJECT_EVENT_LOG, "Event-Log"} + , + {OBJECT_GLOBAL_GROUP, "Global-Group"} + , + {OBJECT_TREND_LOG_MULTIPLE, "Trend-Log-Multiple"} + , + {OBJECT_LOAD_CONTROL, "Load-Control"} + , + {OBJECT_STRUCTURED_VIEW, "Structured-View"} + , + + {0, NULL} +/* Enumerated values 0-127 are reserved for definition by ASHRAE. + Enumerated values 128-1023 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +const char *bactext_object_type_name(int index) +{ + return indtext_by_index_split_default(bacnet_object_type_names, + index, 128, ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_property_names[] = { +/* FIXME: use the enumerations from bacenum.h */ + {PROP_ACKED_TRANSITIONS, "acked-transitions"} + , + {PROP_ACK_REQUIRED, "ack-required"} + , + {PROP_ACTION, "action"} + , + {PROP_ACTION_TEXT, "action-text"} + , + {PROP_ACTIVE_TEXT, "active-text"} + , + {PROP_ACTIVE_VT_SESSIONS, "active-vt-sessions"} + , + {PROP_ALARM_VALUE, "alarm-value"} + , + {PROP_ALARM_VALUES, "alarm-values"} + , + {PROP_ALL, "all"} + , + {PROP_ALL_WRITES_SUCCESSFUL, "all-writes-successful"} + , + {PROP_APDU_SEGMENT_TIMEOUT, "apdu-segment-timeout"} + , + {PROP_APDU_TIMEOUT, "apdu-timeout"} + , + {PROP_APPLICATION_SOFTWARE_VERSION, "application-software-version"} + , + {PROP_ARCHIVE, "archive"} + , + {PROP_BIAS, "bias"} + , + {PROP_CHANGE_OF_STATE_COUNT, "change-of-state-count"} + , + {PROP_CHANGE_OF_STATE_TIME, "change-of-state-time"} + , + {PROP_NOTIFICATION_CLASS, "notification-class"} + , + {PROP_BLANK_1, "(deleted in 135-2001)"} + , + {PROP_CONTROLLED_VARIABLE_REFERENCE, "controlled-variable-reference"} + , + {PROP_CONTROLLED_VARIABLE_UNITS, "controlled-variable-units"} + , + {PROP_CONTROLLED_VARIABLE_VALUE, "controlled-variable-value"} + , + {PROP_COV_INCREMENT, "COV-increment"} + , + {PROP_DATE_LIST, "datelist"} + , + {PROP_DAYLIGHT_SAVINGS_STATUS, "daylight-savings-status"} + , + {PROP_DEADBAND, "deadband"} + , + {PROP_DERIVATIVE_CONSTANT, "derivative-constant"} + , + {PROP_DERIVATIVE_CONSTANT_UNITS, "derivative-constant-units"} + , + {PROP_DESCRIPTION, "description"} + , + {PROP_DESCRIPTION_OF_HALT, "description-of-halt"} + , + {PROP_DEVICE_ADDRESS_BINDING, "device-address-binding"} + , + {PROP_DEVICE_TYPE, "device-type"} + , + {PROP_EFFECTIVE_PERIOD, "effective-period"} + , + {PROP_ELAPSED_ACTIVE_TIME, "elapsed-active-time"} + , + {PROP_ERROR_LIMIT, "error-limit"} + , + {PROP_EVENT_ENABLE, "event-enable"} + , + {PROP_EVENT_STATE, "event-state"} + , + {PROP_EVENT_TYPE, "event-type"} + , + {PROP_EXCEPTION_SCHEDULE, "exception-schedule"} + , + {PROP_FAULT_VALUES, "fault-values"} + , + {PROP_FEEDBACK_VALUE, "feedback-value"} + , + {PROP_FILE_ACCESS_METHOD, "file-access-method"} + , + {PROP_FILE_SIZE, "file-size"} + , + {PROP_FILE_TYPE, "file-type"} + , + {PROP_FIRMWARE_REVISION, "firmware-version"} + , + {PROP_HIGH_LIMIT, "high-limit"} + , + {PROP_INACTIVE_TEXT, "inactive-text"} + , + {PROP_IN_PROCESS, "in-process"} + , + {PROP_INSTANCE_OF, "instance-of"} + , + {PROP_INTEGRAL_CONSTANT, "integral-constant"} + , + {PROP_INTEGRAL_CONSTANT_UNITS, "integral-constant-units"} + , + {PROP_ISSUE_CONFIRMED_NOTIFICATIONS, "issue-confirmednotifications"} + , + {PROP_LIMIT_ENABLE, "limit-enable"} + , + {PROP_LIST_OF_GROUP_MEMBERS, "list-of-group-members"} + , + {PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, + "list-of-object-property-references"} + , + {PROP_LIST_OF_SESSION_KEYS, "list-of-session-keys"} + , + {PROP_LOCAL_DATE, "local-date"} + , + {PROP_LOCAL_TIME, "local-time"} + , + {PROP_LOCATION, "location"} + , + {PROP_LOW_LIMIT, "low-limit"} + , + {PROP_MANIPULATED_VARIABLE_REFERENCE, "manipulated-variable-reference"} + , + {PROP_MAXIMUM_OUTPUT, "maximum-output"} + , + {PROP_MAX_APDU_LENGTH_ACCEPTED, "max-apdu-length-accepted"} + , + {PROP_MAX_INFO_FRAMES, "max-info-frames"} + , + {PROP_MAX_MASTER, "max-master"} + , + {PROP_MAX_PRES_VALUE, "max-pres-value"} + , + {PROP_MINIMUM_OFF_TIME, "minimum-off-time"} + , + {PROP_MINIMUM_ON_TIME, "minimum-on-time"} + , + {PROP_MINIMUM_OUTPUT, "minimum-output"} + , + {PROP_MIN_PRES_VALUE, "min-pres-value"} + , + {PROP_MODEL_NAME, "model-name"} + , + {PROP_MODIFICATION_DATE, "modification-date"} + , + {PROP_NOTIFY_TYPE, "notify-type"} + , + {PROP_NUMBER_OF_APDU_RETRIES, "number-of-APDU-retries"} + , + {PROP_NUMBER_OF_STATES, "number-of-states"} + , + {PROP_OBJECT_IDENTIFIER, "object-identifier"} + , + {PROP_OBJECT_LIST, "object-list"} + , + {PROP_OBJECT_NAME, "object-name"} + , + {PROP_OBJECT_PROPERTY_REFERENCE, "object-property-reference"} + , + {PROP_OBJECT_TYPE, "object-type"} + , + {PROP_OPTIONAL, "optional"} + , + {PROP_OUT_OF_SERVICE, "out-of-service"} + , + {PROP_OUTPUT_UNITS, "output-units"} + , + {PROP_EVENT_PARAMETERS, "event-parameters"} + , + {PROP_POLARITY, "polarity"} + , + {PROP_PRESENT_VALUE, "present-value"} + , + {PROP_PRIORITY, "priority"} + , + {PROP_PRIORITY_ARRAY, "priority-array"} + , + {PROP_PRIORITY_FOR_WRITING, "priority-for-writing"} + , + {PROP_PROCESS_IDENTIFIER, "process-identifier"} + , + {PROP_PROGRAM_CHANGE, "program-change"} + , + {PROP_PROGRAM_LOCATION, "program-location"} + , + {PROP_PROGRAM_STATE, "program-state"} + , + {PROP_PROPORTIONAL_CONSTANT, "proportional-constant"} + , + {PROP_PROPORTIONAL_CONSTANT_UNITS, "proportional-constant-units"} + , + {PROP_PROTOCOL_CONFORMANCE_CLASS, "protocol-conformance-class"} + , + {PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + "protocol-object-types-supported"} + , + {PROP_PROTOCOL_SERVICES_SUPPORTED, "protocol-services-supported"} + , + {PROP_PROTOCOL_VERSION, "protocol-version"} + , + {PROP_READ_ONLY, "read-only"} + , + {PROP_REASON_FOR_HALT, "reason-for-halt"} + , + {PROP_RECIPIENT, "recipient"} + , + {PROP_RECIPIENT_LIST, "recipient-list"} + , + {PROP_RELIABILITY, "reliability"} + , + {PROP_RELINQUISH_DEFAULT, "relinquish-default"} + , + {PROP_REQUIRED, "required"} + , + {PROP_RESOLUTION, "resolution"} + , + {PROP_SEGMENTATION_SUPPORTED, "segmentation-supported"} + , + {PROP_SETPOINT, "setpoint"} + , + {PROP_SETPOINT_REFERENCE, "setpoint-reference"} + , + {PROP_STATE_TEXT, "state-text"} + , + {PROP_STATUS_FLAGS, "status-flags"} + , + {PROP_SYSTEM_STATUS, "system-status"} + , + {PROP_TIME_DELAY, "time-delay"} + , + {PROP_TIME_OF_ACTIVE_TIME_RESET, "time-of-active-time-reset"} + , + {PROP_TIME_OF_STATE_COUNT_RESET, "time-of-state-count-reset"} + , + {PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + "time-synchronization-recipients"} + , + {PROP_UNITS, "units"} + , + {PROP_UPDATE_INTERVAL, "update-interval"} + , + {PROP_UTC_OFFSET, "utc-offset"} + , + {PROP_VENDOR_IDENTIFIER, "vendor-identifier"} + , + {PROP_VENDOR_NAME, "vendor-name"} + , + {PROP_VT_CLASSES_SUPPORTED, "vt-classes-supported"} + , + {PROP_WEEKLY_SCHEDULE, "weekly-schedule"} + , + {PROP_ATTEMPTED_SAMPLES, "attempted-samples"} + , + {PROP_AVERAGE_VALUE, "average-value"} + , + {PROP_BUFFER_SIZE, "buffer-size"} + , + {PROP_CLIENT_COV_INCREMENT, "client-cov-increment"} + , + {PROP_COV_RESUBSCRIPTION_INTERVAL, "cov-resubscription-interval"} + , + {PROP_CURRENT_NOTIFY_TIME, "current-notify-time"} + , + {PROP_EVENT_TIME_STAMPS, "event-time-stamps"} + , + {PROP_LOG_BUFFER, "log-buffer"} + , + {PROP_LOG_DEVICE_OBJECT, "log-device-object-property"} + , + {PROP_ENABLE, "enable"} + , + {PROP_LOG_INTERVAL, "log-interval"} + , + {PROP_MAXIMUM_VALUE, "maximum-value"} + , + {PROP_MINIMUM_VALUE, "minimum-value"} + , + {PROP_NOTIFICATION_THRESHOLD, "notification-threshold"} + , + {PROP_PREVIOUS_NOTIFY_TIME, "previous-notify-time"} + , + {PROP_PROTOCOL_REVISION, "protocol-revision"} + , + {PROP_RECORDS_SINCE_NOTIFICATION, "records-since-notification"} + , + {PROP_RECORD_COUNT, "record-count"} + , + {PROP_START_TIME, "start-time"} + , + {PROP_STOP_TIME, "stop-time"} + , + {PROP_STOP_WHEN_FULL, "stop-when-full"} + , + {PROP_TOTAL_RECORD_COUNT, "total-record-count"} + , + {PROP_VALID_SAMPLES, "valid-samples"} + , + {PROP_WINDOW_INTERVAL, "window-interval"} + , + {PROP_WINDOW_SAMPLES, "window-samples"} + , + {PROP_MAXIMUM_VALUE_TIMESTAMP, "maximum-value-timestamp"} + , + {PROP_MINIMUM_VALUE_TIMESTAMP, "minimum-value-timestamp"} + , + {PROP_VARIANCE_VALUE, "variance-value"} + , + {PROP_ACTIVE_COV_SUBSCRIPTIONS, "active-cov-subscriptions"} + , + {PROP_BACKUP_FAILURE_TIMEOUT, "backup-failure-timeout"} + , + {PROP_CONFIGURATION_FILES, "configuration-files"} + , + {PROP_DATABASE_REVISION, "database-revision"} + , + {PROP_DIRECT_READING, "direct-reading"} + , + {PROP_LAST_RESTORE_TIME, "last-restore-time"} + , + {PROP_MAINTENANCE_REQUIRED, "maintenance-required"} + , + {PROP_MEMBER_OF, "member-of"} + , + {PROP_MODE, "mode"} + , + {PROP_OPERATION_EXPECTED, "operation-expected"} + , + {PROP_SETTING, "setting"} + , + {PROP_SILENCED, "silenced"} + , + {PROP_TRACKING_VALUE, "tracking-value"} + , + {PROP_ZONE_MEMBERS, "zone-members"} + , + {PROP_LIFE_SAFETY_ALARM_VALUES, "life-safety-alarm-values"} + , + {PROP_MAX_SEGMENTS_ACCEPTED, "max-segments-accepted"} + , + {PROP_PROFILE_NAME, "profile-name"} + , + {PROP_AUTO_SLAVE_DISCOVERY, "auto-slave-discovery"} + , + {PROP_MANUAL_SLAVE_ADDRESS_BINDING, "manual-slave-address-binding"} + , + {PROP_SLAVE_ADDRESS_BINDING, "slave-address-binding"} + , + {PROP_SLAVE_PROXY_ENABLE, "slave-proxy-enable"} + , + {PROP_LAST_NOTIFY_TIME, "last-notify-time"} + , + {PROP_SCHEDULE_DEFAULT, "schedule-default"} + , + {PROP_ACCEPTED_MODES, "accepted-modes"} + , + {PROP_ADJUST_VALUE, "adjust-value"} + , + {PROP_COUNT, "count"} + , + {PROP_COUNT_BEFORE_CHANGE, "count-before-change"} + , + {PROP_COUNT_CHANGE_TIME, "count-change-time"} + , + {PROP_COV_PERIOD, "COV-period"} + , + {PROP_INPUT_REFERENCE, "input-reference"} + , + {PROP_LIMIT_MONITORING_INTERVAL, "limit-monitoring-interval"} + , + {PROP_LOGGING_DEVICE, "logging-device"} + , + {PROP_LOGGING_RECORD, "logging-record"} + , + {PROP_PRESCALE, "prescale"} + , + {PROP_PULSE_RATE, "pulse-rate"} + , + {PROP_SCALE, "scale"} + , + {PROP_SCALE_FACTOR, "scale-factor"} + , + {PROP_UPDATE_TIME, "update-time"} + , + {PROP_VALUE_BEFORE_CHANGE, "value-before-change"} + , + {PROP_VALUE_SET, "value-set"} + , + {PROP_VALUE_CHANGE_TIME, "value-change-time"} + , + {PROP_ALIGN_INTERVALS, "align-intervals"} + , + {PROP_GROUP_MEMBER_NAMES, "group-member-names"} + , + {PROP_INTERVAL_OFFSET, "interval-offset"} + , + {PROP_LAST_RESTART_REASON, "last-restart-reason"} + , + {PROP_LOGGING_TYPE, "logging-type"} + , + {PROP_MEMBER_STATUS_FLAGS, "member-status-flags"} + , + {PROP_NOTIFICATION_PERIOD, "notification-period"} + , + {PROP_PREVIOUS_NOTIFY_RECORD, "previous-notify-record"} + , + {PROP_REQUESTED_UPDATE_INTERVAL, "requested-update-interval"} + , + {PROP_RESTART_NOTIFICATION_RECIPIENTS, + "restart-notification-recipients"} + , + {PROP_TIME_OF_DEVICE_RESTART, "time-of-device-restart"} + , + {PROP_TIME_SYNCHRONIZATION_INTERVAL, "time-synchronization-interval"} + , + {PROP_TRIGGER, "trigger"} + , + {PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS, + "UTC-time-synchronization-recipients"} + , + {PROP_NODE_SUBTYPE, "node-subtype"} + , + {PROP_NODE_TYPE, "node-type"} + , + {PROP_STRUCTURED_OBJECT_LIST, "structured-object-list"} + , + {PROP_SUBORDINATE_ANNOTATIONS, "subordinate-annotations"} + , + {PROP_SUBORDINATE_LIST, "subordinate-list"} + , + {PROP_ACTUAL_SHED_LEVEL, "actual-shed-level"} + , + {PROP_DUTY_WINDOW, "duty-window"} + , + {PROP_EXPECTED_SHED_LEVEL, "expected-shed-level"} + , + {PROP_FULL_DUTY_BASELINE, "full-duty-baseline"} + , + {PROP_REQUESTED_SHED_LEVEL, "requested-shed-level"} + , + {PROP_SHED_DURATION, "shed-duration"} + , + {PROP_SHED_LEVEL_DESCRIPTIONS, "shed-level-descriptions"} + , + {PROP_SHED_LEVELS, "shed-levels"} + , + {PROP_STATE_DESCRIPTION, "state-descriptions"} + , + + {0, NULL} + /* Enumerated values 0-511 are reserved for definition by ASHRAE. + Enumerated values 512-4194303 may be used by others subject to the + procedures and constraints described in Clause 23. */ +}; + +const char *bactext_property_name(int index) +{ + return indtext_by_index_split_default(bacnet_property_names, + index, 512, ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +unsigned bactext_property_id(const char *name) +{ + return indtext_by_istring_default(bacnet_property_names, name, 0); +} + +INDTEXT_DATA bacnet_engineering_unit_names[] = { + {UNITS_SQUARE_METERS, "square-meters"} + , + {UNITS_SQUARE_FEET, "square-feet"} + , + {UNITS_MILLIAMPERES, "milliamperes"} + , + {UNITS_AMPERES, "amperes"} + , + {UNITS_OHMS, "ohms"} + , + {UNITS_VOLTS, "volts"} + , + {UNITS_KILOVOLTS, "kilovolts"} + , + {UNITS_MEGAVOLTS, "megavolts"} + , + {UNITS_VOLT_AMPERES, "volt-amperes"} + , + {UNITS_KILOVOLT_AMPERES, "kilovolt-amperes"} + , + {UNITS_MEGAVOLT_AMPERES, "megavolt-amperes"} + , + {UNITS_VOLT_AMPERES_REACTIVE, "volt-amperes-reactive"} + , + {UNITS_KILOVOLT_AMPERES_REACTIVE, "kilovolt-amperes-reactive"} + , + {UNITS_MEGAVOLT_AMPERES_REACTIVE, "megavolt-amperes-reactive"} + , + {UNITS_DEGREES_PHASE, "degrees-phase"} + , + {UNITS_POWER_FACTOR, "power-factor"} + , + {UNITS_JOULES, "joules"} + , + {UNITS_KILOJOULES, "kilojoules"} + , + {UNITS_WATT_HOURS, "watt-hours"} + , + {UNITS_KILOWATT_HOURS, "kilowatt-hours"} + , + {UNITS_BTUS, "btus"} + , + {UNITS_THERMS, "therms"} + , + {UNITS_TON_HOURS, "ton-hours"} + , + {UNITS_JOULES_PER_KILOGRAM_DRY_AIR, "joules-per-kilogram-dry-air"} + , + {UNITS_BTUS_PER_POUND_DRY_AIR, "btus-per-pound-dry-air"} + , + {UNITS_CYCLES_PER_HOUR, "cycles-per-hour"} + , + {UNITS_CYCLES_PER_MINUTE, "cycles-per-minute"} + , + {UNITS_HERTZ, "hertz"} + , + {UNITS_GRAMS_OF_WATER_PER_KILOGRAM_DRY_AIR, + "grams-of-water-per-kilogram-dry-air"} + , + {UNITS_PERCENT_RELATIVE_HUMIDITY, "percent-relative-humidity"} + , + {UNITS_MILLIMETERS, "millimeters"} + , + {UNITS_METERS, "meters"} + , + {UNITS_INCHES, "inches"} + , + {UNITS_FEET, "feet"} + , + {UNITS_WATTS_PER_SQUARE_FOOT, "watts-per-square-foot"} + , + {UNITS_WATTS_PER_SQUARE_METER, "watts-per-square-meter"} + , + {UNITS_LUMENS, "lumens"} + , + {UNITS_LUXES, "luxes"} + , + {UNITS_FOOT_CANDLES, "foot-candles"} + , + {UNITS_KILOGRAMS, "kilograms"} + , + {UNITS_POUNDS_MASS, "pounds-mass"} + , + {UNITS_TONS, "tons"} + , + {UNITS_KILOGRAMS_PER_SECOND, "kilograms-per-second"} + , + {UNITS_KILOGRAMS_PER_MINUTE, "kilograms-per-minute"} + , + {UNITS_KILOGRAMS_PER_HOUR, "kilograms-per-hour"} + , + {UNITS_POUNDS_MASS_PER_MINUTE, "pounds-mass-per-minute"} + , + {UNITS_POUNDS_MASS_PER_HOUR, "pounds-mass-per-hour"} + , + {UNITS_WATTS, "watts"} + , + {UNITS_KILOWATTS, "kilowatts"} + , + {UNITS_MEGAWATTS, "megawatts"} + , + {UNITS_BTUS_PER_HOUR, "btus-per-hour"} + , + {UNITS_HORSEPOWER, "horsepower"} + , + {UNITS_TONS_REFRIGERATION, "tons-refrigeration"} + , + {UNITS_PASCALS, "pascals"} + , + {UNITS_KILOPASCALS, "kilopascals"} + , + {UNITS_BARS, "bars"} + , + {UNITS_POUNDS_FORCE_PER_SQUARE_INCH, "pounds-force-per-square-inch"} + , + {UNITS_CENTIMETERS_OF_WATER, "centimeters-of-water"} + , + {UNITS_INCHES_OF_WATER, "inches-of-water"} + , + {UNITS_MILLIMETERS_OF_MERCURY, "millimeters-of-mercury"} + , + {UNITS_CENTIMETERS_OF_MERCURY, "centimeters-of-mercury"} + , + {UNITS_INCHES_OF_MERCURY, "inches-of-mercury"} + , + {UNITS_DEGREES_CELSIUS, "degrees-celsius"} + , + {UNITS_DEGREES_KELVIN, "degrees-kelvin"} + , + {UNITS_DEGREES_FAHRENHEIT, "degrees-fahrenheit"} + , + {UNITS_DEGREE_DAYS_CELSIUS, "degree-days-celsius"} + , + {UNITS_DEGREE_DAYS_FAHRENHEIT, "degree-days-fahrenheit"} + , + {UNITS_YEARS, "years"} + , + {UNITS_MONTHS, "months"} + , + {UNITS_WEEKS, "weeks"} + , + {UNITS_DAYS, "days"} + , + {UNITS_HOURS, "hours"} + , + {UNITS_MINUTES, "minutes"} + , + {UNITS_SECONDS, "secondS"} + , + {UNITS_METERS_PER_SECOND, "meters-per-second"} + , + {UNITS_KILOMETERS_PER_HOUR, "kilometers-per-hour"} + , + {UNITS_FEET_PER_SECOND, "feet-per-second"} + , + {UNITS_FEET_PER_MINUTE, "feet-per-minute"} + , + {UNITS_MILES_PER_HOUR, "miles-per-hour"} + , + {UNITS_CUBIC_FEET, "cubic-feet"} + , + {UNITS_CUBIC_METERS, "cubic-meters"} + , + {UNITS_IMPERIAL_GALLONS, "imperial-gallons"} + , + {UNITS_LITERS, "liters"} + , + {UNITS_US_GALLONS, "us-gallons"} + , + {UNITS_CUBIC_FEET_PER_MINUTE, "cubic-feet-per-minute"} + , + {UNITS_CUBIC_METERS_PER_SECOND, "cubic-meters-per-second"} + , + {UNITS_IMPERIAL_GALLONS_PER_MINUTE, "imperial-gallons-per-minute"} + , + {UNITS_LITERS_PER_SECOND, "liters-per-second"} + , + {UNITS_LITERS_PER_MINUTE, "liters-per-minute"} + , + {UNITS_US_GALLONS_PER_MINUTE, "us-gallons-per-minute"} + , + {UNITS_DEGREES_ANGULAR, "degrees-angular"} + , + {UNITS_DEGREES_CELSIUS_PER_HOUR, "degrees-celsius-per-hour"} + , + {UNITS_DEGREES_CELSIUS_PER_MINUTE, "degrees-celsius-per-minute"} + , + {UNITS_DEGREES_FAHRENHEIT_PER_HOUR, "degrees-fahrenheit-per-hour"} + , + {UNITS_DEGREES_FAHRENHEIT_PER_MINUTE, "degrees-fahrenheit-per-minute"} + , + {UNITS_NO_UNITS, "no-units"} + , + {UNITS_PARTS_PER_MILLION, "parts-per-million"} + , + {UNITS_PARTS_PER_BILLION, "parts-per-billion"} + , + {UNITS_PERCENT, "percent"} + , + {UNITS_PERCENT_PER_SECOND, "percent-per-second"} + , + {UNITS_PER_MINUTE, "per-minute"} + , + {UNITS_PER_SECOND, "per-second"} + , + {UNITS_PSI_PER_DEGREE_FAHRENHEIT, "psi-per-degree-fahrenheit"} + , + {UNITS_RADIANS, "radians"} + , + {UNITS_REVOLUTIONS_PER_MINUTE, "revolutions-per-minute"} + , + {UNITS_CURRENCY1, "currency1"} + , + {UNITS_CURRENCY2, "currency2"} + , + {UNITS_CURRENCY3, "currency3"} + , + {UNITS_CURRENCY4, "currency4"} + , + {UNITS_CURRENCY5, "currency5"} + , + {UNITS_CURRENCY6, "currency6"} + , + {UNITS_CURRENCY7, "currency7"} + , + {UNITS_CURRENCY8, "currency8"} + , + {UNITS_CURRENCY9, "currency9"} + , + {UNITS_CURRENCY10, "currency10"} + , + {UNITS_SQUARE_INCHES, "square-inches"} + , + {UNITS_SQUARE_CENTIMETERS, "square-centimeters"} + , + {UNITS_BTUS_PER_POUND, "btus_per-pound"} + , + {UNITS_CENTIMETERS, "centimeters"} + , + {UNITS_POUNDS_MASS_PER_SECOND, "pounds-mass-per-second"} + , + {UNITS_DELTA_DEGREES_FAHRENHEIT, "delta-degrees-fahrenheit"} + , + {UNITS_DELTA_DEGREES_KELVIN, "delta-degrees-kelvin"} + , + {UNITS_KILOHMS, "kilohms"} + , + {UNITS_MEGOHMS, "megohms"} + , + {UNITS_MILLIVOLTS, "millivolts"} + , + {UNITS_KILOJOULES_PER_KILOGRAM, "kilojoules-per-kilogram"} + , + {UNITS_MEGAJOULES, "megajoules"} + , + {UNITS_JOULES_PER_DEGREE_KELVIN, "joules-per-degree-kelvin"} + , + {UNITS_JOULES_PER_KILOGRAM_DEGREE_KELVIN, + "joules-per-kilogram-degree-kelvin"} + , + {UNITS_KILOHERTZ, "kilohertz"} + , + {UNITS_MEGAHERTZ, "megahertz"} + , + {UNITS_PER_HOUR, "per-hour"} + , + {UNITS_MILLIWATTS, "milliwatts"} + , + {UNITS_HECTOPASCALS, "hectopascals"} + , + {UNITS_MILLIBARS, "millibars"} + , + {UNITS_CUBIC_METERS_PER_HOUR, "cubic-meters-per-hour"} + , + {UNITS_LITERS_PER_HOUR, "liters-per-hour"} + , + {UNITS_KILOWATT_HOURS_PER_SQUARE_METER, + "kilowatt-hours-per-square-meter"} + , + {UNITS_KILOWATT_HOURS_PER_SQUARE_FOOT, "kilowatt-hours-per-square-foot"} + , + {UNITS_MEGAJOULES_PER_SQUARE_METER, "megajoules-per-square-meter"} + , + {UNITS_MEGAJOULES_PER_SQUARE_FOOT, "megajoules-per-square-foot"} + , + {UNITS_CUBIC_FEET_PER_SECOND, "cubic-feet-per-second"} + , + {UNITS_WATTS_PER_SQUARE_METER_DEGREE_KELVIN, + "watts-per-square-meter-degree-kelvin"} + , + {UNITS_PERCENT_OBSCURATION_PER_FOOT, "percent-obscuration-per-foot"} + , + {UNITS_PERCENT_OBSCURATION_PER_METER, "percent-obscuration-per-meter"} + , + {UNITS_MILLIOHMS, "milliohms"} + , + {UNITS_MEGAWATT_HOURS, "megawatt-hours"} + , + {UNITS_KILO_BTUS, "kilo-btus"} + , + {UNITS_MEGA_BTUS, "mega-btus"} + , + {UNITS_KILOJOULES_PER_KILOGRAM_DRY_AIR, + "kilojoules-per-kilogram-dry-air"} + , + {UNITS_MEGAJOULES_PER_KILOGRAM_DRY_AIR, + "megajoules-per-kilogram-dry-air"} + , + {UNITS_KILOJOULES_PER_DEGREE_KELVIN, "kilojoules-per-degree-Kelvin"} + , + {UNITS_MEGAJOULES_PER_DEGREE_KELVIN, "megajoules-per-degree-Kelvin"} + , + {UNITS_NEWTON, "newton"} + , + {UNITS_GRAMS_PER_SECOND, "grams-per-second"} + , + {UNITS_GRAMS_PER_MINUTE, "grams-per-minute"} + , + {UNITS_TONS_PER_HOUR, "tons-per-hour"} + , + {UNITS_KILO_BTUS_PER_HOUR, "kilo-btus-per-hour"} + , + {UNITS_HUNDREDTHS_SECONDS, "hundredths-seconds"} + , + {UNITS_MILLISECONDS, "milliseconds"} + , + {UNITS_NEWTON_METERS, "newton-meters"} + , + {UNITS_MILLIMETERS_PER_SECOND, "millimeters-per-second"} + , + {UNITS_MILLIMETERS_PER_MINUTE, "millimeters-per-minute"} + , + {UNITS_METERS_PER_MINUTE, "meters-per-minute"} + , + {UNITS_METERS_PER_HOUR, "meters-per-hour"} + , + {UNITS_CUBIC_METERS_PER_MINUTE, "cubic-meters-per-minute"} + , + {UNITS_METERS_PER_SECOND_PER_SECOND, "meters-per-second-per-second"} + , + {UNITS_AMPERES_PER_METER, "amperes-per-meter"} + , + {UNITS_AMPERES_PER_SQUARE_METER, "amperes-per-square-meter"} + , + {UNITS_AMPERE_SQUARE_METERS, "ampere-square-meters"} + , + {UNITS_FARADS, "farads"} + , + {UNITS_HENRYS, "henrys"} + , + {UNITS_OHM_METERS, "ohm-meters"} + , + {UNITS_SIEMENS, "siemens"} + , + {UNITS_SIEMENS_PER_METER, "siemens-per-meter"} + , + {UNITS_TESLAS, "teslas"} + , + {UNITS_VOLTS_PER_DEGREE_KELVIN, "volts-per-degree-Kelvin"} + , + {UNITS_VOLTS_PER_METER, "volts-per-meter"} + , + {UNITS_WEBERS, "webers"} + , + {UNITS_CANDELAS, "candelas"} + , + {UNITS_CANDELAS_PER_SQUARE_METER, "candelas-per-square-meter"} + , + {UNITS_DEGREES_KELVIN_PER_HOUR, "degrees-Kelvin-per-hour"} + , + {UNITS_DEGREES_KELVIN_PER_MINUTE, "degrees-Kelvin-per-minute"} + , + {UNITS_JOULE_SECONDS, "joule-seconds"} + , + {UNITS_RADIANS_PER_SECOND, "radians-per-second"} + , + {UNITS_SQUARE_METERS_PER_NEWTON, "square-meters-per-Newton"} + , + {UNITS_KILOGRAMS_PER_CUBIC_METER, "kilograms-per-cubic-meter"} + , + {UNITS_NEWTON_SECONDS, "newton-seconds"} + , + {UNITS_NEWTONS_PER_METER, "newtons-per-meter"} + , + {UNITS_WATTS_PER_METER_PER_DEGREE_KELVIN, + "watts-per-meter-per-degree-Kelvin"} + , + {0, NULL} +/* Enumerated values 0-255 are reserved for definition by ASHRAE. + Enumerated values 256-65535 may be used by others subject to + the procedures and constraints described in Clause 23. */ +}; + +const char *bactext_engineering_unit_name(int index) +{ + return indtext_by_index_split_default(bacnet_engineering_unit_names, + index, 256, ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_reject_reason_names[] = { + {REJECT_REASON_OTHER, "Other"} + , + {REJECT_REASON_BUFFER_OVERFLOW, "Buffer Overflow"} + , + {REJECT_REASON_INCONSISTENT_PARAMETERS, "Inconsistent Parameters"} + , + {REJECT_REASON_INVALID_PARAMETER_DATA_TYPE, + "Invalid Parameter Data Type"} + , + {REJECT_REASON_INVALID_TAG, "Invalid Tag"} + , + {REJECT_REASON_MISSING_REQUIRED_PARAMETER, "Missing Required Parameter"} + , + {REJECT_REASON_PARAMETER_OUT_OF_RANGE, "Parameter Out of Range"} + , + {REJECT_REASON_TOO_MANY_ARGUMENTS, "Too Many Arguments"} + , + {REJECT_REASON_UNDEFINED_ENUMERATION, "Undefined Enumeration"} + , + {REJECT_REASON_UNRECOGNIZED_SERVICE, "Unrecognized Service"} + , + {0, NULL} +}; + +const char *bactext_reject_reason_name(int index) +{ + return indtext_by_index_split_default(bacnet_reject_reason_names, + index, + FIRST_PROPRIETARY_REJECT_REASON, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_abort_reason_names[] = { + {ABORT_REASON_OTHER, "Other"} + , + {ABORT_REASON_BUFFER_OVERFLOW, "Buffer Overflow"} + , + {ABORT_REASON_INVALID_APDU_IN_THIS_STATE, "Invalid APDU in this State"} + , + {ABORT_REASON_PREEMPTED_BY_HIGHER_PRIORITY_TASK, + "Preempted by Higher Priority Task"} + , + {ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, "Segmentation Not Supported"} + , + {0, NULL} +}; + +const char *bactext_abort_reason_name(int index) +{ + return indtext_by_index_split_default(bacnet_abort_reason_names, + index, + FIRST_PROPRIETARY_ABORT_REASON, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_error_class_names[] = { + {ERROR_CLASS_DEVICE, "device"} + , + {ERROR_CLASS_OBJECT, "object"} + , + {ERROR_CLASS_PROPERTY, "property"} + , + {ERROR_CLASS_RESOURCES, "resources"} + , + {ERROR_CLASS_SECURITY, "security"} + , + {ERROR_CLASS_SERVICES, "services"} + , + {ERROR_CLASS_VT, "vt"} + , + {0, NULL} +}; + +const char *bactext_error_class_name(int index) +{ + return indtext_by_index_split_default(bacnet_error_class_names, + index, + FIRST_PROPRIETARY_ERROR_CLASS, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_error_code_names[] = { + {ERROR_CODE_OTHER, "other"} + , + {ERROR_CODE_AUTHENTICATION_FAILED, "authentication-failed"} + , + {ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED, "character-set-not-supported"} + , + {ERROR_CODE_CONFIGURATION_IN_PROGRESS, "configuration-in-progress"} + , + {ERROR_CODE_DATATYPE_NOT_SUPPORTED, "datatype-not-supported"} + , + {ERROR_CODE_DEVICE_BUSY, "device-busy"} + , + {ERROR_CODE_DUPLICATE_NAME, "duplicate-name"} + , + {ERROR_CODE_DUPLICATE_OBJECT_ID, "duplicate-object-id"} + , + {ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED, + "dynamic-creation-not-supported"} + , + {ERROR_CODE_FILE_ACCESS_DENIED, "file-access-denied"} + , + {ERROR_CODE_INCOMPATIBLE_SECURITY_LEVELS, + "incompatible-security-levels"} + , + {ERROR_CODE_INCONSISTENT_PARAMETERS, "inconsistent-parameters"} + , + {ERROR_CODE_INCONSISTENT_SELECTION_CRITERION, + "inconsistent-selection-criterion"} + , + {ERROR_CODE_INVALID_ARRAY_INDEX, "invalid-array-index"} + , + {ERROR_CODE_INVALID_CONFIGURATION_DATA, "invalid-configuration-data"} + , + {ERROR_CODE_INVALID_DATA_TYPE, "invalid-data-type"} + , + {ERROR_CODE_INVALID_FILE_ACCESS_METHOD, "invalid-file-access-method"} + , + {ERROR_CODE_ERROR_CODE_INVALID_FILE_START_POSITION, + "error-code-invalid-file-start-position"} + , + {ERROR_CODE_INVALID_OPERATOR_NAME, "invalid-operator-name"} + , + {ERROR_CODE_INVALID_PARAMETER_DATA_TYPE, "invalid-parameter-data-type"} + , + {ERROR_CODE_INVALID_TIME_STAMP, "invalid-time-stamp"} + , + {ERROR_CODE_KEY_GENERATION_ERROR, "key-generation-error"} + , + {ERROR_CODE_MISSING_REQUIRED_PARAMETER, "missing-required-parameter"} + , + {ERROR_CODE_NO_OBJECTS_OF_SPECIFIED_TYPE, + "no-objects-of-specified-type"} + , + {ERROR_CODE_NO_SPACE_FOR_OBJECT, "no-space-for-object"} + , + {ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT, + "no-space-to-add-list-element"} + , + {ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY, "no-space-to-write-property"} + , + {ERROR_CODE_NO_VT_SESSIONS_AVAILABLE, "no-vt-sessions-available"} + , + {ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED, + "object-deletion-not-permitted"} + , + {ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS, + "object-identifier-already-exists"} + , + {ERROR_CODE_OPERATIONAL_PROBLEM, "operational-problem"} + , + {ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, + "optional-functionality-not-supported"} + , + {ERROR_CODE_PASSWORD_FAILURE, "password-failure"} + , + {ERROR_CODE_PROPERTY_IS_NOT_A_LIST, "property-is-not-a-list"} + , + {ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY, "property-is-not-an-array"} + , + {ERROR_CODE_READ_ACCESS_DENIED, "read-access-denied"} + , + {ERROR_CODE_SECURITY_NOT_SUPPORTED, "security-not-supported"} + , + {ERROR_CODE_SERVICE_REQUEST_DENIED, "service-request-denied"} + , + {ERROR_CODE_TIMEOUT, "timeout"} + , + {ERROR_CODE_UNKNOWN_OBJECT, "unknown-object"} + , + {ERROR_CODE_UNKNOWN_PROPERTY, "unknown-property"} + , + {ERROR_CODE_RESERVED1, "reserved1"} + , + {ERROR_CODE_UNKNOWN_VT_CLASS, "unknown-vt-class"} + , + {ERROR_CODE_UNKNOWN_VT_SESSION, "unknown-vt-session"} + , + {ERROR_CODE_UNSUPPORTED_OBJECT_TYPE, "unsupported-object-type"} + , + {ERROR_CODE_VALUE_OUT_OF_RANGE, "value-out-of-range"} + , + {ERROR_CODE_VT_SESSION_ALREADY_CLOSED, "vt-session-already-closed"} + , + {ERROR_CODE_VT_SESSION_TERMINATION_FAILURE, + "vt-session-termination-failure"} + , + {ERROR_CODE_WRITE_ACCESS_DENIED, "write-access-denied"} + , + {ERROR_CODE_COV_SUBSCRIPTION_FAILED, "cov-subscription-failed"} + , + {ERROR_CODE_NOT_COV_PROPERTY, "not-cov-property"} + , + {0, NULL} +}; + +const char *bactext_error_code_name(int index) +{ + return indtext_by_index_split_default(bacnet_error_code_names, + index, + FIRST_PROPRIETARY_ERROR_CLASS, + ASHRAE_Reserved_String, Vendor_Proprietary_String); +} + +INDTEXT_DATA bacnet_month_names[] = { + {1, "January"} + , + {2, "February"} + , + {3, "March"} + , + {4, "April"} + , + {5, "May"} + , + {6, "June"} + , + {7, "July"} + , + {8, "August"} + , + {9, "September"} + , + {10, "October"} + , + {11, "November"} + , + {12, "December"} + , + {255, "Any Month"} + , + {0, NULL} +}; + +const char *bactext_month_name(int index) +{ + return indtext_by_index_default(bacnet_month_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_week_of_month_names[] = { + {1, "days numbered 1-7"} + , + {2, "days numbered 8-14"} + , + {3, "days numbered 15-21"} + , + {4, "days numbered 22-28"} + , + {5, "days numbered 29-31"} + , + {6, "last 7 days of this month"} + , + {255, "any week of this month"} + , + {0, NULL} +}; + +const char *bactext_week_of_month_name(int index) +{ + return indtext_by_index_default(bacnet_week_of_month_names, + index, ASHRAE_Reserved_String); +} + +/* note: different than DaysOfWeek bit string where 0=monday */ +INDTEXT_DATA bacnet_day_of_week_names[] = { + {1, "Monday"} + , + {2, "Tuesday"} + , + {3, "Wednesday"} + , + {4, "Thursday"} + , + {5, "Friday"} + , + {6, "Saturday"} + , + {7, "Sunday"} + , + {255, "any day of week"} + , + {0, NULL} +}; + +const char *bactext_day_of_week_name(int index) +{ + return indtext_by_index_default(bacnet_day_of_week_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_event_state_names[] = { + {EVENT_STATE_NORMAL, "normal"} + , + {EVENT_STATE_FAULT, "fault"} + , + {EVENT_STATE_OFFNORMAL, "offnormal"} + , + {EVENT_STATE_HIGH_LIMIT, "high limit"} + , + {EVENT_STATE_LOW_LIMIT, "low limit"} + , + {0, NULL} +}; + +const char *bactext_event_state_name(int index) +{ + return indtext_by_index_default(bacnet_event_state_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_binary_present_value_names[] = { + {BINARY_INACTIVE, "inactive"} + , + {BINARY_ACTIVE, "active"} + , + {0, NULL} +}; + +const char *bactext_binary_present_value_name(int index) +{ + return indtext_by_index_default(bacnet_binary_present_value_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_reliability_names[] = { + {RELIABILITY_NO_FAULT_DETECTED, "no-fault-detected"} + , + {RELIABILITY_NO_SENSOR, "no-sensor"} + , + {RELIABILITY_OVER_RANGE, "over-range"} + , + {RELIABILITY_UNDER_RANGE, "under-range"} + , + {RELIABILITY_OPEN_LOOP, "open-loop"} + , + {RELIABILITY_SHORTED_LOOP, "shorted-loop"} + , + {RELIABILITY_NO_OUTPUT, "no-output"} + , + {RELIABILITY_UNRELIABLE_OTHER, "unreliable-other"} + , + {RELIABILITY_PROCESS_ERROR, "process-error"} + , + {RELIABILITY_MULTI_STATE_FAULT, "mult-state-fault"} + , + {RELIABILITY_CONFIGURATION_ERROR, "configuration-error"} + , + {0, NULL} +}; + +const char *bactext_reliability_name(int index) +{ + return indtext_by_index_default(bacnet_reliability_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_device_status_names[] = { + {STATUS_OPERATIONAL, "operational"} + , + {STATUS_OPERATIONAL_READ_ONLY, "operational-read-only"} + , + {STATUS_DOWNLOAD_REQUIRED, "download-required"} + , + {STATUS_DOWNLOAD_IN_PROGRESS, "download-in-progress"} + , + {STATUS_NON_OPERATIONAL, "non-operational"} + , + {0, NULL} +}; + +const char *bactext_device_status_name(int index) +{ + return indtext_by_index_default(bacnet_device_status_names, + index, ASHRAE_Reserved_String); +} + +INDTEXT_DATA bacnet_segmentation_names[] = { + {SEGMENTATION_BOTH, "segmented-both"} + , + {SEGMENTATION_TRANSMIT, "segmented-transmit"} + , + {SEGMENTATION_RECEIVE, "segmented-receive"} + , + {SEGMENTATION_NONE, "no-segmentation"} + , + {0, NULL} +}; + +const char *bactext_segmentation_name(int index) +{ + return indtext_by_index_default(bacnet_segmentation_names, + index, ASHRAE_Reserved_String); +} diff --git a/bacnet-stack-0-3-0/bactext.h b/bacnet-stack-0-3-0/bactext.h new file mode 100644 index 00000000..2e961c7e --- /dev/null +++ b/bacnet-stack-0-3-0/bactext.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/bigend.c b/bacnet-stack-0-3-0/bigend.c new file mode 100644 index 00000000..222327b9 --- /dev/null +++ b/bacnet-stack-0-3-0/bigend.c @@ -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); +} diff --git a/bacnet-stack-0-3-0/bigend.h b/bacnet-stack-0-3-0/bigend.h new file mode 100644 index 00000000..c82b3a65 --- /dev/null +++ b/bacnet-stack-0-3-0/bigend.h @@ -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 diff --git a/bacnet-stack-0-3-0/bip.c b/bacnet-stack-0-3-0/bip.c new file mode 100644 index 00000000..c7c4ae0d --- /dev/null +++ b/bacnet-stack-0-3-0/bip.c @@ -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 /* for standard integer types uint8_t etc. */ +#include /* 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; +} diff --git a/bacnet-stack-0-3-0/bip.h b/bacnet-stack-0-3-0/bip.h new file mode 100644 index 00000000..21e4ac05 --- /dev/null +++ b/bacnet-stack-0-3-0/bip.h @@ -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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/bits.h b/bacnet-stack-0-3-0/bits.h new file mode 100644 index 00000000..55671e17 --- /dev/null +++ b/bacnet-stack-0-3-0/bits.h @@ -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 diff --git a/bacnet-stack-0-3-0/bvlc.c b/bacnet-stack-0-3-0/bvlc.c new file mode 100644 index 00000000..731f851b --- /dev/null +++ b/bacnet-stack-0-3-0/bvlc.c @@ -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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include /* 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? */ +/* FIXME: is sockaddr_in host or network order? */ +void bvlc_bacnet_to_internet_address( struct sockaddr_in *sin, /* source internet address */ + BACNET_ADDRESS * src) +{ /* returns the BACnet source address */ + int len = 0; + if (src && sin) + { + if (src->mac_len == 6) + { + len = decode_unsigned32(&src->mac[0], &sin->sin_addr.s_addr); + len += decode_unsigned16(&src->mac[4], &sin->sin_port); + } + } + return; + } + void bvlc_bdt_forward_npdu( struct sockaddr_in *sin, /* the source address */ + uint8_t * npdu, /* the NPDU */ + uint16_t npdu_length) +{ /* length of the NPDU */ + uint8_t mtu[MAX_MPDU] = { + 0}; + int mtu_len = 0; + int bytes_sent = 0; + unsigned i = 0; /* loop counter */ + struct sockaddr_in bip_dest; + + /* assumes that the driver has already been initialized */ + if (bip_socket() < 0) + return; + mtu_len = + bvlc_encode_forwarded_npdu( &mtu[0], sin, npdu, npdu_length); + + /* load destination IP address */ + bip_dest.sin_family = AF_INET; + + /* loop through the BDT and send one to each entry, except us */ + for (i = 0; i < MAX_BBMD_ENTRIES; i++) + { + if (BBMD_Table[i].valid) + { + + /* The B/IP address to which the Forwarded-NPDU message is + sent is formed by inverting the broadcast distribution + mask in the BDT entry and logically ORing it with the + BBMD address of the same entry. */ + bip_dest.sin_addr.s_addr = + htonl(((~BBMD_Table[i].broadcast_mask. + s_addr) | BBMD_Table[i].dest_address.s_addr)); + bip_dest.sin_port = htons(BBMD_Table[i].dest_port); + + /* Send the packet */ + bytes_sent = + sendto(bip_socket(), (char *) mtu, mtu_len, 0, + (struct sockaddr *) bip_dest, sizeof(struct sockaddr)); + } + return bytes_sent; + } + void bvlc_fdt_forward_npdu( struct sockaddr_in *sin, /* the source address */ + uint8_t * npdu, /* returns the NPDU */ + uint16_t max_npdu) { /* amount of space available in the NPDU */ + } uint16_t bvlc_handler( BACNET_ADDRESS * src, /* returns the source address */ + uint8_t * npdu, /* returns the NPDU */ + uint16_t max_npdu, /* amount of space available in the NPDU */ + 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); + int function_type = 0; + + /* 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; + function_type = buf[1]; + + /* decode the length of the PDU - length is inclusive of BVLC */ + (void) decode_unsigned16(&buf[2], &npdu_len); + + /* subtract off the BVLC header */ + npdu_len -= 4; + switch (function_type) + { + case BVLC_RESULT: + break; + case BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE: + + /* Upon receipt of a BVLL Write-Broadcast-Distribution-Table + message, a BBMD shall attempt to create or replace its BDT, + depending on whether or not a BDT has previously existed. + If the creation or replacement of the BDT is successful, the BBMD + shall return a BVLC-Result message to the originating device with + a result code of X'0000'. Otherwise, the BBMD shall return a + BVLC-Result message to the originating device with a result code + of X'0010' indicating that the write attempt has failed. */ + break; + case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE: + break; + case BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK: + break; + case BVLC_FORWARDED_NPDU: + + /* Upon receipt of a BVLL Forwarded-NPDU message, a BBMD shall + process it according to whether it was received from a peer + BBMD as the result of a directed broadcast or a unicast + transmission. A BBMD may ascertain the method by which Forwarded- + NPDU messages will arrive by inspecting the broadcast distribution + mask field in its own BDT entry since all BDTs are required + to be identical. If the message arrived via directed broadcast, + it was also received by the other devices on the BBMD's subnet. In + this case the BBMD merely retransmits the message directly to each + foreign device currently in the BBMD's FDT. If the + message arrived via a unicast transmission it has not yet been + received by the other devices on the BBMD's subnet. In this case, + the message is sent to the devices on the BBMD's subnet using the + B/IP broadcast address as well as to each foreign device + currently in the BBMD's FDT. A BBMD on a subnet with no other + BACnet devices may omit the broadcast using the B/IP + broadcast address. The method by which a BBMD determines whether + or not other BACnet devices are present is a local matter. */ + bvlc_broadcast_npdu(&sin, &buf[4], npdu_len); + bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len); + break; + case BVLC_REGISTER_FOREIGN_DEVICE: + break; + case BVLC_READ_FOREIGN_DEVICE_TABLE: + break; + case BVLC_READ_FOREIGN_DEVICE_TABLE_ACK: + break; + case BVLC_DELETE_FOREIGN_DEVICE_TABLE_ENTRY: + break; + case BVLC_DISTRIBUTE_BROADCAST_TO_NETWORK: + bvlc_broadcast_forward_npdu(&sin, &buf[4], npdu_len); + bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len); + break; + case BVLC_ORIGINAL_UNICAST_NPDU: + + /* ignore messages from me */ + if (sin.sin_addr.s_addr == BIP_Address.s_addr) + npdu_len = 0; + + else { + bvlc_internet_to_bacnet_address(src, &sin); + + /* copy the buffer into the PDU */ + if (npdu_len < max_npdu) + memmove(&npdu[0], &buf[4], npdu_len); + + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + else + npdu_len = 0; + } + break; + case BVLC_ORIGINAL_BROADCAST_NPDU: + + /* Upon receipt of a BVLL Original-Broadcast-NPDU message, + a BBMD shall construct a BVLL Forwarded-NPDU message and + send it to each IP subnet in its BDT with the exception + of its own. The B/IP address to which the Forwarded-NPDU + message is sent is formed by inverting the broadcast + distribution mask in the BDT entry and logically ORing it + with the BBMD address of the same entry. This process + produces either the directed broadcast address of the remote + subnet or the unicast address of the BBMD on that subnet + depending on the contents of the broadcast distribution + mask. See J.4.3.2.. In addition, the received BACnet NPDU + shall be sent directly to each foreign device currently in + the BBMD's FDT also using the BVLL Forwarded-NPDU message. */ + bvlc_internet_to_bacnet_address(src, &sin); + + /* copy the buffer into the PDU */ + if (npdu_len < max_npdu) + memmove(&npdu[0], &buf[4], npdu_len); + + /* ignore packets that are too large */ + /* clients should check my max-apdu first */ + else + npdu_len = 0; + + /* if BDT or FDT entries exist, Forward the NPDU */ + bvlc_bdt_forward_npdu(&sin, &buf[4], npdu_len); + bvlc_fdt_forward_npdu(&sin, &buf[4], npdu_len); + break; + default: + break; + } + return npdu_len; + } + +#ifdef TEST +#include +#include +#include "ctest.h" + void testBVLC(Test * pTest) { + (void) pTest; + } +#ifdef TEST_BBMD + int main(void) { + Test * pTest; + bool rc; + pTest = ct_create("BACnet Virtual Link Control", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testBVLC); + assert(rc); + + /* configure output */ + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + return 0; + } + +#endif /* TEST_BBMD */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/bvlc.cbp b/bacnet-stack-0-3-0/bvlc.cbp new file mode 100644 index 00000000..18334ed3 --- /dev/null +++ b/bacnet-stack-0-3-0/bvlc.cbp @@ -0,0 +1,98 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/bvlc.h b/bacnet-stack-0-3-0/bvlc.h new file mode 100644 index 00000000..3b2756e5 --- /dev/null +++ b/bacnet-stack-0-3-0/bvlc.h @@ -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 /* for standard integer types uint8_t etc. */ +#include /* 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 /* */ diff --git a/bacnet-stack-0-3-0/bytes.h b/bacnet-stack-0-3-0/bytes.h new file mode 100644 index 00000000..f1c360ab --- /dev/null +++ b/bacnet-stack-0-3-0/bytes.h @@ -0,0 +1,70 @@ +/* Defines the bit/byte/word/long conversions that are used in code */ + + +#ifndef BYTES_H +#define BYTES_H + +#include + +#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 */ diff --git a/bacnet-stack-0-3-0/comment.sh b/bacnet-stack-0-3-0/comment.sh new file mode 100755 index 00000000..84ee554e --- /dev/null +++ b/bacnet-stack-0-3-0/comment.sh @@ -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 + diff --git a/bacnet-stack-0-3-0/config.h b/bacnet-stack-0-3-0/config.h new file mode 100644 index 00000000..1c0233f3 --- /dev/null +++ b/bacnet-stack-0-3-0/config.h @@ -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 diff --git a/bacnet-stack-0-3-0/cov.c b/bacnet-stack-0-3-0/cov.c new file mode 100644 index 00000000..8aad9590 --- /dev/null +++ b/bacnet-stack-0-3-0/cov.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/cov.cbp b/bacnet-stack-0-3-0/cov.cbp new file mode 100644 index 00000000..a4265572 --- /dev/null +++ b/bacnet-stack-0-3-0/cov.cbp @@ -0,0 +1,80 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/cov.h b/bacnet-stack-0-3-0/cov.h new file mode 100644 index 00000000..f70e3372 --- /dev/null +++ b/bacnet-stack-0-3-0/cov.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/cov.mak b/bacnet-stack-0-3-0/cov.mak new file mode 100644 index 00000000..2d5b9069 --- /dev/null +++ b/bacnet-stack-0-3-0/cov.mak @@ -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 diff --git a/bacnet-stack-0-3-0/crc.c b/bacnet-stack-0-3-0/crc.c new file mode 100644 index 00000000..7ebeadb0 --- /dev/null +++ b/bacnet-stack-0-3-0/crc.c @@ -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 +#include +#include + +/* 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 +#include +#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 diff --git a/bacnet-stack-0-3-0/crc.h b/bacnet-stack-0-3-0/crc.h new file mode 100644 index 00000000..c0af37fc --- /dev/null +++ b/bacnet-stack-0-3-0/crc.h @@ -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 +#include + +#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 diff --git a/bacnet-stack-0-3-0/crc.ide b/bacnet-stack-0-3-0/crc.ide new file mode 100644 index 00000000..d7b2030c Binary files /dev/null and b/bacnet-stack-0-3-0/crc.ide differ diff --git a/bacnet-stack-0-3-0/crc.mak b/bacnet-stack-0-3-0/crc.mak new file mode 100644 index 00000000..e5bb862f --- /dev/null +++ b/bacnet-stack-0-3-0/crc.mak @@ -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 + diff --git a/bacnet-stack-0-3-0/datalink.h b/bacnet-stack-0-3-0/datalink.h new file mode 100644 index 00000000..7ce29f1f --- /dev/null +++ b/bacnet-stack-0-3-0/datalink.h @@ -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 diff --git a/bacnet-stack-0-3-0/datetime.c b/bacnet-stack-0-3-0/datetime.c new file mode 100644 index 00000000..50812b50 --- /dev/null +++ b/bacnet-stack-0-3-0/datetime.c @@ -0,0 +1,713 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include +#include +#include +#include +#include +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/datetime.h b/bacnet-stack-0-3-0/datetime.h new file mode 100644 index 00000000..c2145b6c --- /dev/null +++ b/bacnet-stack-0-3-0/datetime.h @@ -0,0 +1,112 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef DATE_TIME_H +#define DATE_TIME_H + +#include +#include + +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 */ diff --git a/bacnet-stack-0-3-0/datetime.ide b/bacnet-stack-0-3-0/datetime.ide new file mode 100644 index 00000000..89b2b3f7 Binary files /dev/null and b/bacnet-stack-0-3-0/datetime.ide differ diff --git a/bacnet-stack-0-3-0/datetime.mak b/bacnet-stack-0-3-0/datetime.mak new file mode 100644 index 00000000..864a9de9 --- /dev/null +++ b/bacnet-stack-0-3-0/datetime.mak @@ -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 diff --git a/bacnet-stack-0-3-0/dcc.c b/bacnet-stack-0-3-0/dcc.c new file mode 100644 index 00000000..10d6a883 --- /dev/null +++ b/bacnet-stack-0-3-0/dcc.c @@ -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 +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/dcc.h b/bacnet-stack-0-3-0/dcc.h new file mode 100644 index 00000000..c4557120 --- /dev/null +++ b/bacnet-stack-0-3-0/dcc.h @@ -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 +#include +#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 diff --git a/bacnet-stack-0-3-0/dcc.mak b/bacnet-stack-0-3-0/dcc.mak new file mode 100644 index 00000000..53e9c02a --- /dev/null +++ b/bacnet-stack-0-3-0/dcc.mak @@ -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 diff --git a/bacnet-stack-0-3-0/demo/dcc/Makefile b/bacnet-stack-0-3-0/demo/dcc/Makefile new file mode 100644 index 00000000..71384196 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/dcc/Makefile @@ -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 + diff --git a/bacnet-stack-0-3-0/demo/dcc/main.c b/bacnet-stack-0-3-0/demo/dcc/main.c new file mode 100644 index 00000000..2b7d7c17 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/dcc/main.c @@ -0,0 +1,282 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include /* for time */ +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/dcc/makefile.b32 b/bacnet-stack-0-3-0/demo/dcc/makefile.b32 new file mode 100644 index 00000000..21db35d1 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/dcc/makefile.b32 @@ -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 diff --git a/bacnet-stack-0-3-0/demo/dcc/makefile.g++ b/bacnet-stack-0-3-0/demo/dcc/makefile.g++ new file mode 100644 index 00000000..d8a63f55 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/dcc/makefile.g++ @@ -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 + diff --git a/bacnet-stack-0-3-0/demo/dcc/tmake.pro b/bacnet-stack-0-3-0/demo/dcc/tmake.pro new file mode 100644 index 00000000..1cb88954 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/dcc/tmake.pro @@ -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 diff --git a/bacnet-stack-0-3-0/demo/handler/client.h b/bacnet-stack-0-3-0/demo/handler/client.h new file mode 100644 index 00000000..459efee2 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/client.h @@ -0,0 +1,90 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/demo/handler/h_arf.c b/bacnet-stack-0-3-0/demo/handler/h_arf.c new file mode 100644 index 00000000..8ef2a2b2 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_arf.c @@ -0,0 +1,185 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_arf_a.c b/bacnet-stack-0-3-0/demo/handler/h_arf_a.c new file mode 100644 index 00000000..1562c0e5 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_arf_a.c @@ -0,0 +1,93 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#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 diff --git a/bacnet-stack-0-3-0/demo/handler/h_dcc.c b/bacnet-stack-0-3-0/demo/handler/h_dcc.c new file mode 100644 index 00000000..c671aa86 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_dcc.c @@ -0,0 +1,128 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_iam.c b/bacnet-stack-0-3-0/demo/handler/h_iam.c new file mode 100644 index 00000000..58737886 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_iam.c @@ -0,0 +1,80 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_ihave.c b/bacnet-stack-0-3-0/demo/handler/h_ihave.c new file mode 100644 index 00000000..d1d39a71 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_ihave.c @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_rd.c b/bacnet-stack-0-3-0/demo/handler/h_rd.c new file mode 100644 index 00000000..3b169adc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_rd.c @@ -0,0 +1,130 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_rp.c b/bacnet-stack-0-3-0/demo/handler/h_rp.c new file mode 100644 index 00000000..8662b4ef --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_rp.c @@ -0,0 +1,368 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_rp_a.c b/bacnet-stack-0-3-0/demo/handler/h_rp_a.c new file mode 100644 index 00000000..617229ef --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_rp_a.c @@ -0,0 +1,114 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#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); +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_rp_tiny.c b/bacnet-stack-0-3-0/demo/handler/h_rp_tiny.c new file mode 100644 index 00000000..e45ce5cc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_rp_tiny.c @@ -0,0 +1,122 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_ts.c b/bacnet-stack-0-3-0/demo/handler/h_ts.c new file mode 100644 index 00000000..bd695110 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_ts.c @@ -0,0 +1,82 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_whohas.c b/bacnet-stack-0-3-0/demo/handler/h_whohas.c new file mode 100644 index 00000000..0352aa0c --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_whohas.c @@ -0,0 +1,85 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_whois.c b/bacnet-stack-0-3-0/demo/handler/h_whois.c new file mode 100644 index 00000000..477cc529 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_whois.c @@ -0,0 +1,66 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/h_wp.c b/bacnet-stack-0-3-0/demo/handler/h_wp.c new file mode 100644 index 00000000..2be77f93 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/h_wp.c @@ -0,0 +1,333 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/handlers.h b/bacnet-stack-0-3-0/demo/handler/handlers.h new file mode 100644 index 00000000..b11a5b20 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/handlers.h @@ -0,0 +1,103 @@ +/************************************************************************** +* +* Copyright (C) 2005-2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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 diff --git a/bacnet-stack-0-3-0/demo/handler/noserv.c b/bacnet-stack-0-3-0/demo/handler/noserv.c new file mode 100644 index 00000000..6de3d71b --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/noserv.c @@ -0,0 +1,68 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#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 +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_arfs.c b/bacnet-stack-0-3-0/demo/handler/s_arfs.c new file mode 100644 index 00000000..4e4f117d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_arfs.c @@ -0,0 +1,112 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_awfs.c b/bacnet-stack-0-3-0/demo/handler/s_awfs.c new file mode 100644 index 00000000..f116c34a --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_awfs.c @@ -0,0 +1,126 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_dcc.c b/bacnet-stack-0-3-0/demo/handler/s_dcc.c new file mode 100644 index 00000000..1efebe75 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_dcc.c @@ -0,0 +1,109 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_ihave.c b/bacnet-stack-0-3-0/demo/handler/s_ihave.c new file mode 100644 index 00000000..a50fb726 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_ihave.c @@ -0,0 +1,83 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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 +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_rd.c b/bacnet-stack-0-3-0/demo/handler/s_rd.c new file mode 100644 index 00000000..f72fe3bf --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_rd.c @@ -0,0 +1,107 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_rp.c b/bacnet-stack-0-3-0/demo/handler/s_rp.c new file mode 100644 index 00000000..3a5c1f2f --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_rp.c @@ -0,0 +1,114 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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; +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_ts.c b/bacnet-stack-0-3-0/demo/handler/s_ts.c new file mode 100644 index 00000000..98130261 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_ts.c @@ -0,0 +1,100 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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 +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_whohas.c b/bacnet-stack-0-3-0/demo/handler/s_whohas.c new file mode 100644 index 00000000..5ff4fef8 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_whohas.c @@ -0,0 +1,118 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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 +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_whois.c b/bacnet-stack-0-3-0/demo/handler/s_whois.c new file mode 100644 index 00000000..e67e9a30 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_whois.c @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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 +} diff --git a/bacnet-stack-0-3-0/demo/handler/s_wp.c b/bacnet-stack-0-3-0/demo/handler/s_wp.c new file mode 100644 index 00000000..7643289a --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/s_wp.c @@ -0,0 +1,148 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#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); +} diff --git a/bacnet-stack-0-3-0/demo/handler/txbuf.c b/bacnet-stack-0-3-0/demo/handler/txbuf.c new file mode 100644 index 00000000..25a50b53 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/txbuf.c @@ -0,0 +1,30 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include "config.h" +#include "datalink.h" + +uint8_t Handler_Transmit_Buffer[MAX_MPDU] = { 0 }; diff --git a/bacnet-stack-0-3-0/demo/handler/txbuf.h b/bacnet-stack-0-3-0/demo/handler/txbuf.h new file mode 100644 index 00000000..4b12ea63 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/handler/txbuf.h @@ -0,0 +1,35 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include "config.h" +#include "datalink.h" + +extern uint8_t Handler_Transmit_Buffer[MAX_MPDU]; + +#endif diff --git a/bacnet-stack-0-3-0/demo/object/ai.c b/bacnet-stack-0-3-0/demo/object/ai.c new file mode 100644 index 00000000..35d5197d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ai.c @@ -0,0 +1,197 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#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 +#include +#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 */ diff --git a/bacnet-stack-0-3-0/demo/object/ai.h b/bacnet-stack-0-3-0/demo/object/ai.h new file mode 100644 index 00000000..d940be71 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ai.h @@ -0,0 +1,55 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#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 diff --git a/bacnet-stack-0-3-0/demo/object/ai.mak b/bacnet-stack-0-3-0/demo/object/ai.mak new file mode 100755 index 00000000..529cb4cc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ai.mak @@ -0,0 +1,35 @@ +#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_ANALOG_INPUT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + demo/object/ai.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = analog_input + +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 diff --git a/bacnet-stack-0-3-0/demo/object/ao.c b/bacnet-stack-0-3-0/demo/object/ao.c new file mode 100644 index 00000000..e0d4ee2d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ao.c @@ -0,0 +1,469 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Analog Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_ANALOG_OUTPUTS 4 + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define AO_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define AO_RELINQUISH_DEFAULT 0 +/* Here is our Priority Array. They are supposed to be Real, but */ +/* we don't have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t + Analog_Output_Level[MAX_ANALOG_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Analog_Output_Out_Of_Service[MAX_ANALOG_OUTPUTS]; + +/* we need to have our arrays initialized before answering any calls */ +static bool Analog_Output_Initialized = false; + +void Analog_Output_Init(void) +{ + unsigned i, j; + + if (!Analog_Output_Initialized) { + Analog_Output_Initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_ANALOG_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Analog_Output_Level[i][j] = AO_LEVEL_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Output_Valid_Instance(uint32_t object_instance) +{ + Analog_Output_Init(); + if (object_instance < MAX_ANALOG_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Output_Count(void) +{ + Analog_Output_Init(); + return MAX_ANALOG_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Output_Index_To_Instance(unsigned index) +{ + Analog_Output_Init(); + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Output_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_OUTPUTS; + + Analog_Output_Init(); + if (object_instance < MAX_ANALOG_OUTPUTS) + index = object_instance; + + return index; +} + +float Analog_Output_Present_Value(uint32_t object_instance) +{ + float value = AO_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Analog_Output_Init(); + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { + value = Analog_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +unsigned Analog_Output_Present_Value_Priority(uint32_t object_instance) +{ + unsigned index = 0; /* instance to index conversion */ + unsigned i = 0; /* loop counter */ + unsigned priority = 0; /* return value */ + + Analog_Output_Init(); + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Analog_Output_Level[index][i] != AO_LEVEL_NULL) { + priority = i + 1; + break; + } + } + } + + return priority; +} + +bool Analog_Output_Present_Value_Set(uint32_t object_instance, + float value, unsigned priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value >= 0.0) && (value <= 100.0)) { + Analog_Output_Level[index][priority] = (uint8_t) value; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +bool Analog_Output_Present_Value_Relinquish(uint32_t object_instance, + int priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Output_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_OUTPUTS) { + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ )) { + Analog_Output_Level[index][priority] = AO_LEVEL_NULL; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } + } + + return status; +} + +/* note: the object name must be unique within this device */ +char *Analog_Output_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_OUTPUTS) { + sprintf(text_string, "ANALOG OUTPUT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Analog_Output_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Analog_Output_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_ANALOG_OUTPUT, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Output_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_OUTPUT); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Output_Present_Value(object_instance); + apdu_len = encode_tagged_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + 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: + object_index = Analog_Output_Instance_To_Index(object_instance); + state = Analog_Output_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_UNITS: + apdu_len = encode_tagged_enumerated(&apdu[0], UNITS_PERCENT); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = + Analog_Output_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Analog_Output_Level[object_index][i] == AO_LEVEL_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + real_value = Analog_Output_Level[object_index][i]; + len = encode_tagged_real(&apdu[apdu_len], real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = + Analog_Output_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Analog_Output_Level[object_index][array_index - 1] == + AO_LEVEL_NULL) + apdu_len = encode_tagged_null(&apdu[0]); + else { + real_value = + Analog_Output_Level[object_index][array_index - 1]; + apdu_len = encode_tagged_real(&apdu[0], real_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = AO_RELINQUISH_DEFAULT; + apdu_len = encode_tagged_real(&apdu[0], real_value); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + uint8_t level = AO_LEVEL_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Analog_Output_Init(); + if (!Analog_Output_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + status = + Analog_Output_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority); + if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = AO_LEVEL_NULL; + object_index = + Analog_Output_Instance_To_Index(wp_data->object_instance); + status = + Analog_Output_Present_Value_Relinquish(wp_data-> + object_instance, wp_data->priority); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Output_Instance_To_Index(wp_data->object_instance); + Analog_Output_Out_Of_Service[object_index] = + value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalogOutput(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; + + + len = Analog_Output_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_OUTPUT); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_OUTPUT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalogOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_INPUT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/ao.h b/bacnet-stack-0-3-0/demo/object/ao.h new file mode 100644 index 00000000..1169ea21 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ao.h @@ -0,0 +1,68 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AO_H +#define AO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Analog_Output_Valid_Instance(uint32_t object_instance); + unsigned Analog_Output_Count(void); + uint32_t Analog_Output_Index_To_Instance(unsigned index); + char *Analog_Output_Name(uint32_t object_instance); + float Analog_Output_Present_Value(uint32_t object_instance); + unsigned Analog_Output_Present_Value_Priority(uint32_t + object_instance); + bool Analog_Output_Present_Value_Set(uint32_t object_instance, + float value, unsigned priority); + bool Analog_Output_Present_Value_Relinquish(uint32_t object_instance, + int priority); + + + int Analog_Output_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); + + bool Analog_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testAnalogOutput(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/ao.mak b/bacnet-stack-0-3-0/demo/object/ao.mak new file mode 100755 index 00000000..d5beabbf --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/ao.mak @@ -0,0 +1,38 @@ +#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_ANALOG_OUTPUT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/ao.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = analog_output + +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 diff --git a/bacnet-stack-0-3-0/demo/object/av.c b/bacnet-stack-0-3-0/demo/object/av.c new file mode 100644 index 00000000..022ce72c --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/av.c @@ -0,0 +1,418 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 Value Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_ANALOG_VALUES 4 + +/* we choose to have a NULL level in our system represented by */ +/* a particular value. When the priorities are not in use, they */ +/* will be relinquished (i.e. set to the NULL level). */ +#define ANALOG_LEVEL_NULL 255 +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define ANALOG_RELINQUISH_DEFAULT 0 +/* Here is our Priority Array. They are supposed to be Real, but */ +/* we don't have that kind of memory, so we will use a single byte */ +/* and load a Real for returning the value when asked. */ +static uint8_t Analog_Value_Level[MAX_ANALOG_VALUES][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Analog_Value_Out_Of_Service[MAX_ANALOG_VALUES]; + +/* we need to have our arrays initialized before answering any calls */ +static bool Analog_Value_Initialized = false; + +void Analog_Value_Init(void) +{ + unsigned i, j; + + if (!Analog_Value_Initialized) { + Analog_Value_Initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_ANALOG_VALUES; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Analog_Value_Level[i][j] = ANALOG_LEVEL_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Value_Valid_Instance(uint32_t object_instance) +{ + Analog_Value_Init(); + if (object_instance < MAX_ANALOG_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Value_Count(void) +{ + Analog_Value_Init(); + return MAX_ANALOG_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Value_Index_To_Instance(unsigned index) +{ + Analog_Value_Init(); + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + Analog_Value_Init(); + if (object_instance < MAX_ANALOG_VALUES) + index = object_instance; + + return index; +} + +static float Analog_Value_Present_Value(uint32_t object_instance) +{ + float value = ANALOG_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Analog_Value_Init(); + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Analog_Value_Level[index][i] != ANALOG_LEVEL_NULL) { + value = Analog_Value_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Analog_Value_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_ANALOG_VALUES) { + sprintf(text_string, "ANALOG VALUE %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Analog_Value_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float) 1.414; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Analog_Value_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_ANALOG_VALUE, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Analog_Value_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_VALUE); + break; + case PROP_PRESENT_VALUE: + real_value = Analog_Value_Present_Value(object_instance); + apdu_len = encode_tagged_real(&apdu[0], real_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + 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: + object_index = Analog_Value_Instance_To_Index(object_instance); + state = Analog_Value_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_UNITS: + apdu_len = encode_tagged_enumerated(&apdu[0], UNITS_PERCENT); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = Analog_Value_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Analog_Value_Level[object_index][i] == + ANALOG_LEVEL_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + real_value = Analog_Value_Level[object_index][i]; + len = encode_tagged_real(&apdu[apdu_len], real_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = Analog_Value_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Analog_Value_Level[object_index][array_index - 1] == + ANALOG_LEVEL_NULL) + apdu_len = encode_tagged_null(&apdu[0]); + else { + real_value = + Analog_Value_Level[object_index][array_index - 1]; + apdu_len = encode_tagged_real(&apdu[0], real_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + real_value = ANALOG_RELINQUISH_DEFAULT; + apdu_len = encode_tagged_real(&apdu[0], real_value); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint8_t level = ANALOG_LEVEL_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Analog_Value_Init(); + if (!Analog_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_REAL) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Real >= 0.0) && (value.type.Real <= 100.0)) { + level = (uint8_t) value.type.Real; + object_index = + Analog_Value_Instance_To_Index(wp_data-> + object_instance); + priority--; + Analog_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = ANALOG_LEVEL_NULL; + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Analog_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Analog_Value_Instance_To_Index(wp_data->object_instance); + Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testAnalog_Value(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_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Analog_Value_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_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_ANALOG_VALUE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Analog Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testAnalog_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_ANALOG_VALUE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/av.h b/bacnet-stack-0-3-0/demo/object/av.h new file mode 100644 index 00000000..d89a52d1 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/av.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef AV_H +#define AV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Analog_Value_Valid_Instance(uint32_t object_instance); + unsigned Analog_Value_Count(void); + uint32_t Analog_Value_Index_To_Instance(unsigned index); + char *Analog_Value_Name(uint32_t object_instance); + + int Analog_Value_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); + + bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testAnalog_Value(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/av.mak b/bacnet-stack-0-3-0/demo/object/av.mak new file mode 100644 index 00000000..c9af62bd --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/av.mak @@ -0,0 +1,38 @@ +#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_ANALOG_VALUE -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/av.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = analog_value + +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 diff --git a/bacnet-stack-0-3-0/demo/object/bacfile.c b/bacnet-stack-0-3-0/demo/object/bacfile.c new file mode 100644 index 00000000..4e00f8cd --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bacfile.c @@ -0,0 +1,376 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "bacapp.h" +#include "datalink.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "tsm.h" +#include "device.h" +#include "arf.h" +#include "awf.h" + +typedef struct { + uint32_t instance; + char *filename; +} BACNET_FILE_LISTING; + +static BACNET_FILE_LISTING BACnet_File_Listing[] = { + {0, "test.log"}, + {1, "script.txt"}, + {2, "bacenum.h"}, + {0, NULL} /* last file indication */ +}; + +char *bacfile_name(uint32_t instance) +{ + uint32_t index = 0; + char *filename = NULL; + + /* linear search for file instance match */ + while (BACnet_File_Listing[index].filename) { + if (BACnet_File_Listing[index].instance == instance) { + filename = BACnet_File_Listing[index].filename; + break; + } + index++; + } + + return filename; +} + +bool bacfile_valid_instance(uint32_t object_instance) +{ + return bacfile_name(object_instance) ? true : false; +} + +uint32_t bacfile_count(void) +{ + uint32_t index = 0; + + /* linear search for file instance match */ + while (BACnet_File_Listing[index].filename) { + index++; + } + + return index; +} + +uint32_t bacfile_index_to_instance(unsigned find_index) +{ + uint32_t instance = BACNET_MAX_INSTANCE + 1; + uint32_t index = 0; + + /* bounds checking... */ + while (BACnet_File_Listing[index].filename) { + if (index == find_index) { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +static long fsize(FILE * pFile) +{ + long size = 0; + long origin = 0; + + if (pFile) { + origin = ftell(pFile); + fseek(pFile, 0L, SEEK_END); + size = ftell(pFile); + fseek(pFile, origin, SEEK_SET); + } + return (size); +} + +static unsigned bacfile_file_size(uint32_t object_instance) +{ + char *pFilename = NULL; + FILE *pFile = NULL; + unsigned file_size = 0; + + pFilename = bacfile_name(object_instance); + if (pFilename) { + pFile = fopen(pFilename, "rb"); + if (pFile) { + file_size = fsize(pFile); + fclose(pFile); + } + } + + return file_size; +} + +/* return the number of bytes used, or -1 on error */ +int bacfile_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 */ + char text_string[32] = { "" }; + BACNET_CHARACTER_STRING char_string; + BACNET_DATE bdate; + BACNET_TIME btime; + + (void) array_index; + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], + OBJECT_FILE, object_instance); + break; + case PROP_OBJECT_NAME: + sprintf(text_string, "FILE %d", object_instance); + characterstring_init_ansi(&char_string, text_string); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_FILE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + bacfile_name(object_instance)); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FILE_TYPE: + characterstring_init_ansi(&char_string, "TEXT"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FILE_SIZE: + apdu_len = encode_tagged_unsigned(&apdu[0], + bacfile_file_size(object_instance)); + break; + case PROP_MODIFICATION_DATE: + /* FIXME: get the actual value instead of April Fool's Day */ + bdate.year = 2006; /* AD */ + bdate.month = 4; /* 1=Jan */ + bdate.day = 1; /* 1..31 */ + bdate.wday = 6; /* 1=Monday */ + apdu_len = encode_tagged_date(&apdu[0], &bdate); + /* FIXME: get the actual value */ + btime.hour = 7; + btime.min = 0; + btime.sec = 3; + btime.hundredths = 1; + apdu_len += encode_tagged_time(&apdu[apdu_len], &btime); + break; + case PROP_ARCHIVE: + /* 12.13.8 Archive + This property, of type BOOLEAN, indicates whether the File + object has been saved for historical or backup purposes. This + property shall be logical TRUE only if no changes have been + made to the file data by internal processes or through File + Access Services since the last time the object was archived. + */ + /* FIXME: get the actual value: note it may be inverse... */ + apdu_len = encode_tagged_boolean(&apdu[0], true); + break; + case PROP_READ_ONLY: + /* FIXME: get the actual value */ + apdu_len = encode_tagged_boolean(&apdu[0], true); + break; + case PROP_FILE_ACCESS_METHOD: + apdu_len = encode_tagged_enumerated(&apdu[0], FILE_STREAM_ACCESS); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!bacfile_valid_instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_ARCHIVE: + /* 12.13.8 Archive + This property, of type BOOLEAN, indicates whether the File + object has been saved for historical or backup purposes. This + property shall be logical TRUE only if no changes have been + made to the file data by internal processes or through File + Access Services since the last time the object was archived. */ + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + if (value.type.Boolean) { + /* FIXME: do something to wp_data->object_instance */ + } else { + /* FIXME: do something to wp_data->object_instance */ + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_FILE_SIZE: + /* If the file size can be changed by writing to the file, + and File_Access_Method is STREAM_ACCESS, then this property + shall be writable. */ + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* FIXME: do something with value.type.Unsigned + to wp_data->object_instance */ + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + + +uint32_t bacfile_instance(char *filename) +{ + uint32_t index = 0; + uint32_t instance = BACNET_MAX_INSTANCE + 1; + + /* linear search for filename match */ + while (BACnet_File_Listing[index].filename) { + if (strcmp(BACnet_File_Listing[index].filename, filename) == 0) { + instance = BACnet_File_Listing[index].instance; + break; + } + index++; + } + + return instance; +} + +#if TSM_ENABLED +/* this is one way to match up the invoke ID with */ +/* the file ID from the AtomicReadFile request. */ +/* Another way would be to store the */ +/* invokeID and file instance in a list or table */ +/* when the request was sent */ +uint32_t bacfile_instance_from_tsm(uint8_t invokeID) +{ + BACNET_NPDU_DATA npdu_data = { 0 }; /* dummy for getting npdu length */ + BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 }; + uint8_t service_choice = 0; + uint8_t *service_request = NULL; + uint16_t service_request_len = 0; + BACNET_ADDRESS dest; /* where the original packet was destined */ + uint8_t apdu[MAX_PDU] = { 0 }; /* original APDU packet */ + uint16_t apdu_len = 0; /* original APDU packet length */ + uint16_t len = 0; /* apdu header length */ + BACNET_ATOMIC_READ_FILE_DATA data = { 0 }; + uint32_t object_instance = BACNET_MAX_INSTANCE + 1; /* return value */ + bool found = false; + + found = tsm_get_transaction_pdu(invokeID, &dest, &npdu_data, &apdu[0], + &apdu_len); + if (found) { + if (!npdu_data.network_layer_message + && npdu_data.data_expecting_reply + && (apdu[0] == PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) { + len = + apdu_decode_confirmed_service_request(&apdu[0], + apdu_len, &service_data, &service_choice, + &service_request, &service_request_len); + if (service_choice == SERVICE_CONFIRMED_ATOMIC_READ_FILE) { + len = arf_decode_service_request(service_request, + service_request_len, &data); + if (len > 0) { + if (data.object_type == OBJECT_FILE) + object_instance = data.object_instance; + } + } + } + } + + return object_instance; +} +#endif + +bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA * data) +{ + char *pFilename = NULL; + bool found = false; + FILE *pFile = NULL; + size_t len = 0; + + pFilename = bacfile_name(data->object_instance); + if (pFilename) { + found = true; + pFile = fopen(pFilename, "rb"); + if (pFile) { + (void) fseek(pFile, + data->type.stream.fileStartPosition, SEEK_SET); + len = fread(octetstring_value(&data->fileData), 1, + data->type.stream.requestedOctetCount, pFile); + if (len < data->type.stream.requestedOctetCount) + data->endOfFile = true; + else + data->endOfFile = false; + octetstring_truncate(&data->fileData, len); + fclose(pFile); + } else { + octetstring_truncate(&data->fileData, 0); + data->endOfFile = true; + } + } else { + octetstring_truncate(&data->fileData, 0); + data->endOfFile = true; + } + + return found; +} diff --git a/bacnet-stack-0-3-0/demo/object/bacfile.h b/bacnet-stack-0-3-0/demo/object/bacfile.h new file mode 100644 index 00000000..a80714db --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bacfile.h @@ -0,0 +1,79 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#ifndef BACFILE_H +#define BACFILE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "apdu.h" +#include "arf.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + char *bacfile_name(uint32_t instance); + bool bacfile_valid_instance(uint32_t object_instance); + uint32_t bacfile_count(void); + uint32_t bacfile_index_to_instance(unsigned find_index); + uint32_t bacfile_instance(char *filename); +#if TSM_ENABLED +/* this is one way to match up the invoke ID with */ +/* the file ID from the AtomicReadFile request. */ +/* Another way would be to store the */ +/* invokeID and file instance in a list or table */ +/* when the request was sent */ + uint32_t bacfile_instance_from_tsm(uint8_t invokeID); +#endif + +/* AtomicReadFile ACK helper */ + bool bacfile_read_data(BACNET_ATOMIC_READ_FILE_DATA * data); + +/* handling for read property service */ + int bacfile_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); + +/* handling for write property service */ + bool bacfile_write_property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/bi.c b/bacnet-stack-0-3-0/demo/object/bi.c new file mode 100644 index 00000000..8e7211bc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bi.c @@ -0,0 +1,243 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ + +#define MAX_BINARY_INPUTS 5 + +static BACNET_BINARY_PV Present_Value[MAX_BINARY_INPUTS]; + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Input_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_INPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Input_Count(void) +{ + return MAX_BINARY_INPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Input_Index_To_Instance(unsigned index) +{ + return index; +} + +void Binary_Input_Init(void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + + /* initialize all the values */ + for (i = 0; i < MAX_BINARY_INPUTS; i++) { + Present_Value[i] = BINARY_INACTIVE; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Input_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_BINARY_INPUTS; + + Binary_Input_Init(); + if (object_instance < MAX_BINARY_INPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Input_Present_Value(uint32_t + object_instance) +{ + BACNET_BINARY_PV value = BINARY_INACTIVE; + unsigned index = 0; + + Binary_Input_Init(); + index = Binary_Input_Instance_To_Index(object_instance); + if (index < MAX_BINARY_INPUTS) + value = Present_Value[index]; + + return value; +} + +char *Binary_Input_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + Binary_Input_Init(); + if (object_instance < MAX_BINARY_INPUTS) { + sprintf(text_string, "BINARY INPUT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu length, or -1 on error */ +/* assumption - object already exists, and has been bounds checked */ +int Binary_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; + BACNET_POLARITY polarity = POLARITY_NORMAL; + + + (void) array_index; + Binary_Input_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_BINARY_INPUT, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + /* note: object name must be unique in our device */ + characterstring_init_ansi(&char_string, + Binary_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_BINARY_INPUT); + break; + case PROP_PRESENT_VALUE: + /* note: you need to look up the actual value */ + apdu_len = + encode_tagged_enumerated(&apdu[0], + Binary_Input_Present_Value(object_instance)); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + 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: + /* note: see the details in the standard on how to use this */ + 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_POLARITY: + apdu_len = encode_tagged_enumerated(&apdu[0], polarity); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBinaryInput(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_BINARY_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 = Binary_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_BINARY_INPUT); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_BINARY_INPUT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary Input", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinaryInput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/bi.h b/bacnet-stack-0-3-0/demo/object/bi.h new file mode 100644 index 00000000..9b5d5187 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bi.h @@ -0,0 +1,55 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BI_H +#define BI_H + +#include +#include +#include "bacdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Binary_Input_Valid_Instance(uint32_t object_instance); + unsigned Binary_Input_Count(void); + uint32_t Binary_Input_Index_To_Instance(unsigned index); + char *Binary_Input_Name(uint32_t object_instance); + + int Binary_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 testBinaryInput(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/bi.mak b/bacnet-stack-0-3-0/demo/object/bi.mak new file mode 100644 index 00000000..7bae3cbc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bi.mak @@ -0,0 +1,35 @@ +#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_BINARY_INPUT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + demo/object/bi.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = binary_input + +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 diff --git a/bacnet-stack-0-3-0/demo/object/bo.c b/bacnet-stack-0-3-0/demo/object/bo.c new file mode 100644 index 00000000..a323b33b --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bo.c @@ -0,0 +1,421 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_BINARY_OUTPUTS 6 + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static BACNET_BINARY_PV + Binary_Output_Level[MAX_BINARY_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Binary_Output_Out_Of_Service[MAX_BINARY_OUTPUTS]; + +void Binary_Output_Init(void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Output_Level[i][j] = BINARY_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Output_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Output_Count(void) +{ + return MAX_BINARY_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Output_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_BINARY_OUTPUTS; + + if (object_instance < MAX_BINARY_OUTPUTS) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Output_Present_Value(uint32_t + object_instance) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Binary_Output_Init(); + index = Binary_Output_Instance_To_Index(object_instance); + if (index < MAX_BINARY_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Binary_Output_Level[index][i] != BINARY_NULL) { + value = Binary_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Output_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_BINARY_OUTPUTS) { + sprintf(text_string, "BINARY OUTPUT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Output_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Binary_Output_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_BINARY_OUTPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Output_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_BINARY_OUTPUT); + break; + case PROP_PRESENT_VALUE: + present_value = Binary_Output_Present_Value(object_instance); + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = Binary_Output_Instance_To_Index(object_instance); + state = Binary_Output_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + apdu_len = encode_tagged_enumerated(&apdu[0], polarity); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = + Binary_Output_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Binary_Output_Level[object_index][i] == BINARY_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + present_value = Binary_Output_Level[object_index][i]; + len = + encode_tagged_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = + Binary_Output_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Binary_Output_Level[object_index][array_index] == + BINARY_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + present_value = + Binary_Output_Level[object_index][array_index]; + len = + encode_tagged_enumerated(&apdu[apdu_len], + present_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Binary_Output_Init(); + if (!Binary_Output_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated >= MIN_BINARY_PV) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = value.type.Enumerated; + object_index = + Binary_Output_Instance_To_Index(wp_data-> + object_instance); + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = BINARY_NULL; + object_index = + Binary_Output_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Output_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Output_Instance_To_Index(wp_data->object_instance); + Binary_Output_Out_Of_Service[object_index] = + value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBinaryOutput(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_BINARY_OUTPUT; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Binary_Output_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_BINARY_OUTPUT); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_BINARY_OUTPUT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinaryOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/bo.h b/bacnet-stack-0-3-0/demo/object/bo.h new file mode 100644 index 00000000..7898186e --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bo.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BO_H +#define BO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Binary_Output_Valid_Instance(uint32_t object_instance); + unsigned Binary_Output_Count(void); + uint32_t Binary_Output_Index_To_Instance(unsigned index); + char *Binary_Output_Name(uint32_t object_instance); + + int Binary_Output_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); + + bool Binary_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testBinaryOutput(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/bo.mak b/bacnet-stack-0-3-0/demo/object/bo.mak new file mode 100755 index 00000000..84cefbab --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bo.mak @@ -0,0 +1,38 @@ +#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_BINARY_OUTPUT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/bo.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = binary_output + +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 diff --git a/bacnet-stack-0-3-0/demo/object/bv.c b/bacnet-stack-0-3-0/demo/object/bv.c new file mode 100644 index 00000000..b915d8dc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bv.c @@ -0,0 +1,417 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Binary Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_BINARY_VALUES 2 + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define RELINQUISH_DEFAULT BINARY_INACTIVE +/* Here is our Priority Array.*/ +static BACNET_BINARY_PV + Binary_Value_Level[MAX_BINARY_VALUES][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Binary_Value_Out_Of_Service[MAX_BINARY_VALUES]; + +void Binary_Value_Init(void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_BINARY_VALUES; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Binary_Value_Level[i][j] = BINARY_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Binary_Value_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_BINARY_VALUES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Binary_Value_Count(void) +{ + return MAX_BINARY_VALUES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Binary_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Binary_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_BINARY_VALUES; + + if (object_instance < MAX_BINARY_VALUES) + index = object_instance; + + return index; +} + +static BACNET_BINARY_PV Binary_Value_Present_Value(uint32_t + object_instance) +{ + BACNET_BINARY_PV value = RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Binary_Value_Init(); + index = Binary_Value_Instance_To_Index(object_instance); + if (index < MAX_BINARY_VALUES) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Binary_Value_Level[index][i] != BINARY_NULL) { + value = Binary_Value_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Binary_Value_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_BINARY_VALUES) { + sprintf(text_string, "BINARY VALUE %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Binary_Value_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_BINARY_PV present_value = BINARY_INACTIVE; + BACNET_POLARITY polarity = POLARITY_NORMAL; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Binary_Value_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_BINARY_VALUE, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Binary_Value_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_BINARY_VALUE); + break; + case PROP_PRESENT_VALUE: + present_value = Binary_Value_Present_Value(object_instance); + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_EVENT_STATE: + /* note: see the details in the standard on how to use this */ + apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = Binary_Value_Instance_To_Index(object_instance); + state = Binary_Value_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_POLARITY: + apdu_len = encode_tagged_enumerated(&apdu[0], polarity); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = Binary_Value_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Binary_Value_Level[object_index][i] == BINARY_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + present_value = Binary_Value_Level[object_index][i]; + len = + encode_tagged_enumerated(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = Binary_Value_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Binary_Value_Level[object_index][array_index] == + BINARY_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + present_value = + Binary_Value_Level[object_index][array_index]; + len = + encode_tagged_enumerated(&apdu[apdu_len], + present_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = RELINQUISH_DEFAULT; + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + BACNET_BINARY_PV level = BINARY_NULL; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Binary_Value_Init(); + if (!Binary_Value_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Enumerated >= MIN_BINARY_PV) && + (value.type.Enumerated <= MAX_BINARY_PV)) { + level = value.type.Enumerated; + object_index = + Binary_Value_Instance_To_Index(wp_data-> + object_instance); + priority--; + Binary_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = BINARY_NULL; + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Binary_Value_Level[object_index][priority] = level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Binary_Value_Instance_To_Index(wp_data->object_instance); + Binary_Value_Out_Of_Service[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testBinary_Value(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_BINARY_VALUE; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Binary_Value_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_BINARY_VALUE); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_BINARY_VALUE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Binary_Value", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testBinary_Value); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_VALUE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/bv.h b/bacnet-stack-0-3-0/demo/object/bv.h new file mode 100644 index 00000000..30c0184d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bv.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef BV_H +#define BV_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Binary_Value_Valid_Instance(uint32_t object_instance); + unsigned Binary_Value_Count(void); + uint32_t Binary_Value_Index_To_Instance(unsigned index); + char *Binary_Value_Name(uint32_t object_instance); + + int Binary_Value_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); + + bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testBinary_Value(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/bv.mak b/bacnet-stack-0-3-0/demo/object/bv.mak new file mode 100644 index 00000000..2d24d359 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/bv.mak @@ -0,0 +1,38 @@ +#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_BINARY_VALUE -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/bv.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = binary_value + +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 diff --git a/bacnet-stack-0-3-0/demo/object/device.c b/bacnet-stack-0-3-0/demo/object/device.c new file mode 100644 index 00000000..7ac7d35d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/device.c @@ -0,0 +1,1114 @@ +/************************************************************************** +* +* Copyright (C) 2005,2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "ai.h" /* object list dependency */ +#include "ao.h" /* object list dependency */ +#include "av.h" /* object list dependency */ +#include "bi.h" /* object list dependency */ +#include "bo.h" /* object list dependency */ +#include "bv.h" /* object list dependency */ +#include "lc.h" /* object list dependency */ +#include "lsp.h" /* object list dependency */ +#include "mso.h" /* object list dependency */ +#include "wp.h" /* write property handling */ +#include "device.h" /* me */ +#if BACFILE +#include "bacfile.h" /* object list dependency */ +#endif + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 0; +static char Object_Name[16] = "SimpleServer"; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static char Vendor_Name[16] = "ASHRAE"; +/* FIXME: your vendor id assigned by ASHRAE */ +static uint16_t Vendor_Identifier = 0; +static char Model_Name[16] = "GNU"; +static char Firmware_Revision[16] = "1.0"; +static char Application_Software_Version[16] = "1.0"; +static char Location[16] = "USA"; +static char Description[16] = "server"; +/* static uint8_t Protocol_Version = 1; - constant, not settable */ +/* static uint8_t Protocol_Revision = 4; - constant, not settable */ +/* Protocol_Services_Supported - dynamically generated */ +/* Protocol_Object_Types_Supported - in RP encoding */ +/* Object_List - dynamically generated */ +/* static uint16_t Max_APDU_Length_Accepted = MAX_APDU; - constant */ +/* static BACNET_SEGMENTATION Segmentation_Supported = SEGMENTATION_NONE; */ +/* static uint8_t Max_Segments_Accepted = 0; */ +/* VT_Classes_Supported */ +/* Active_VT_Sessions */ +BACNET_TIME Local_Time; /* rely on OS, if there is one */ +BACNET_DATE Local_Date; /* rely on OS, if there is one */ +/* BACnet UTC is inverse of standard offset - i.e. relative to local */ +static int UTC_Offset = 5; +static bool Daylight_Savings_Status = false; /* rely on OS */ +/* APDU_Segment_Timeout */ +static uint16_t APDU_Timeout = 3000; +static uint8_t Number_Of_APDU_Retries = 3; +/* List_Of_Session_Keys */ +/* Time_Synchronization_Recipients */ +/* Max_Master - rely on MS/TP subsystem, if there is one */ +/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */ +/* Device_Address_Binding - required, but relies on binding cache */ +static uint8_t Database_Revision = 0; +/* Configuration_Files */ +/* Last_Restore_Time */ +/* Backup_Failure_Timeout */ +/* Active_COV_Subscriptions */ +/* Slave_Proxy_Enable */ +/* Manual_Slave_Address_Binding */ +/* Auto_Slave_Discovery */ +/* Slave_Address_Binding */ +/* Profile_Name */ + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) + Object_Instance_Number = object_id; + else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +const char *Device_Object_Name(void) +{ + return Object_Name; +} + +bool Device_Set_Object_Name(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Object_Name)) { + memmove(Object_Name, name, length); + Object_Name[length] = 0; + status = true; + } + + return status; +} + +BACNET_DEVICE_STATUS Device_System_Status(void) +{ + return System_Status; +} + +void Device_Set_System_Status(BACNET_DEVICE_STATUS status) +{ + /* FIXME: bounds check? */ + System_Status = status; +} + +const char *Device_Vendor_Name(void) +{ + return Vendor_Name; +} + +bool Device_Set_Vendor_Name(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Vendor_Name)) { + memmove(Vendor_Name, name, length); + Vendor_Name[length] = 0; + status = true; + } + + return status; +} + +uint16_t Device_Vendor_Identifier(void) +{ + return Vendor_Identifier; +} + +void Device_Set_Vendor_Identifier(uint16_t vendor_id) +{ + Vendor_Identifier = vendor_id; +} + +const char *Device_Model_Name(void) +{ + return Model_Name; +} + +bool Device_Set_Model_Name(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Model_Name)) { + memmove(Model_Name, name, length); + Model_Name[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Firmware_Revision(void) +{ + return Firmware_Revision; +} + +bool Device_Set_Firmware_Revision(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Firmware_Revision)) { + memmove(Firmware_Revision, name, length); + Firmware_Revision[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Application_Software_Version(void) +{ + return Application_Software_Version; +} + +bool Device_Set_Application_Software_Version(const char *name, + size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Application_Software_Version)) { + memmove(Application_Software_Version, name, length); + Application_Software_Version[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Description(void) +{ + return Description; +} + +bool Device_Set_Description(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Description)) { + memmove(Description, name, length); + Description[length] = 0; + status = true; + } + + return status; +} + +const char *Device_Location(void) +{ + return Location; +} + +bool Device_Set_Location(const char *name, size_t length) +{ + bool status = false; /*return value */ + + if (length < sizeof(Location)) { + memmove(Location, name, length); + Location[length] = 0; + status = true; + } + + return status; +} + +uint8_t Device_Protocol_Version(void) +{ + return BACNET_PROTOCOL_VERSION; +} + +uint8_t Device_Protocol_Revision(void) +{ + return BACNET_PROTOCOL_REVISION; +} + +uint16_t Device_Max_APDU_Length_Accepted(void) +{ + return MAX_APDU; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported(void) +{ + return SEGMENTATION_NONE; +} + +uint16_t Device_APDU_Timeout(void) +{ + return APDU_Timeout; +} + +/* in milliseconds */ +void Device_Set_APDU_Timeout(uint16_t timeout) +{ + APDU_Timeout = timeout; +} + +uint8_t Device_Number_Of_APDU_Retries(void) +{ + return Number_Of_APDU_Retries; +} + +void Device_Set_Number_Of_APDU_Retries(uint8_t retries) +{ + Number_Of_APDU_Retries = retries; +} + +uint8_t Device_Database_Revision(void) +{ + return Database_Revision; +} + +void Device_Set_Database_Revision(uint8_t revision) +{ + Database_Revision = revision; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count(void) +{ + unsigned count = 1; + + count += Analog_Input_Count(); + count += Analog_Output_Count(); + count += Analog_Value_Count(); + count += Binary_Input_Count(); + count += Binary_Output_Count(); + count += Binary_Value_Count(); + count += Life_Safety_Point_Count(); + count += Load_Control_Count(); + count += Multistate_Output_Count(); +#if BACFILE + count += bacfile_count(); +#endif + + return count; +} + +bool Device_Object_List_Identifier(unsigned array_index, + int *object_type, uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index = array_index - 2; + object_count = Analog_Input_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_INPUT; + *instance = Analog_Input_Index_To_Instance(object_index); + status = true; + } + } + /* analog output objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Analog_Output_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_OUTPUT; + *instance = Analog_Output_Index_To_Instance(object_index); + status = true; + } + } + /* analog value objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Analog_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_VALUE; + *instance = Analog_Value_Index_To_Instance(object_index); + status = true; + } + } + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Input_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_INPUT; + *instance = Binary_Input_Index_To_Instance(object_index); + status = true; + } + } + /* binary output objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Output_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_OUTPUT; + *instance = Binary_Output_Index_To_Instance(object_index); + status = true; + } + } + /* binary value objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } + /* life safety point objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Life_Safety_Point_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_LIFE_SAFETY_POINT; + *instance = Life_Safety_Point_Index_To_Instance(object_index); + status = true; + } + } + /* load control objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Load_Control_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_LOAD_CONTROL; + *instance = Load_Control_Index_To_Instance(object_index); + status = true; + } + } + /* multi-state output objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Multistate_Output_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_MULTI_STATE_OUTPUT; + *instance = Multistate_Output_Index_To_Instance(object_index); + status = true; + } + } + /* file objects */ +#if BACFILE + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = bacfile_count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_FILE; + *instance = bacfile_index_to_instance(object_index); + status = true; + } + } +#endif + + return status; +} + +bool Device_Valid_Object_Name(const char *object_name, + int *object_type, uint32_t * object_instance) +{ + bool found = false; + int type = 0; + uint32_t instance; + unsigned max_objects = 0, i = 0; + bool check_id = false; + char *name = NULL; + + max_objects = Device_Object_List_Count(); + for (i = 0; i < max_objects; i++) { + check_id = Device_Object_List_Identifier(i, &type, &instance); + if (check_id) { + name = Device_Valid_Object_Id(type, instance); + if (strcmp(name, object_name) == 0) { + found = true; + if (object_type) + *object_type = type; + if (object_instance) + *object_instance = instance; + break; + } + } + } + + return found; +} + +/* returns the name or NULL if not found */ +char *Device_Valid_Object_Id(int object_type, uint32_t object_instance) +{ + char *name = NULL; /* return value */ + + switch (object_type) { + case OBJECT_ANALOG_INPUT: + name = Analog_Input_Name(object_instance); + break; + case OBJECT_ANALOG_OUTPUT: + name = Analog_Output_Name(object_instance); + break; + case OBJECT_ANALOG_VALUE: + name = Analog_Value_Name(object_instance); + break; + case OBJECT_BINARY_INPUT: + name = Binary_Input_Name(object_instance); + break; + case OBJECT_BINARY_OUTPUT: + name = Binary_Output_Name(object_instance); + break; + case OBJECT_BINARY_VALUE: + name = Binary_Value_Name(object_instance); + break; + case OBJECT_LIFE_SAFETY_POINT: + name = Life_Safety_Point_Name(object_instance); + break; + case OBJECT_LOAD_CONTROL: + name = Load_Control_Name(object_instance); + break; + case OBJECT_MULTI_STATE_OUTPUT: + name = Multistate_Output_Name(object_instance); + break; +#if BACFILE + case OBJECT_FILE: + name = bacfile_name(object_instance); + break; +#endif + case OBJECT_DEVICE: + if (object_instance == Object_Instance_Number) + name = Object_Name; + break; + default: + break; + } + + return name; +} + +/* return the length of the apdu encoded or -1 for error or + -2 for abort message */ +int Device_Encode_Property_APDU(uint8_t * apdu, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, Object_Name); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, Description); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = encode_tagged_enumerated(&apdu[0], System_Status); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, Vendor_Name); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = encode_tagged_unsigned(&apdu[0], Vendor_Identifier); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, Model_Name); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, Firmware_Revision); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, + Application_Software_Version); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + /* FIXME: if you support time */ + case PROP_LOCAL_TIME: + /* FIXME: get the actual value */ + Local_Time.hour = 7; + Local_Time.min = 0; + Local_Time.sec = 3; + Local_Time.hundredths = 1; + apdu_len = encode_tagged_time(&apdu[0], &Local_Time); + break; + /* FIXME: if you support time */ + case PROP_UTC_OFFSET: + /* NOTE: if your UTC offset is -5, then BACnet UTC offset is 5 */ + apdu_len = encode_tagged_signed(&apdu[0], UTC_Offset); + break; + /* FIXME: if you support date */ + case PROP_LOCAL_DATE: + /* FIXME: get the actual value instead of April Fool's Day */ + Local_Date.year = 2006; /* AD */ + Local_Date.month = 4; /* 1=Jan */ + Local_Date.day = 1; /* 1..31 */ + Local_Date.wday = 6; /* 1=Monday */ + apdu_len = encode_tagged_date(&apdu[0], &Local_Date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + apdu_len = + encode_tagged_boolean(&apdu[0], Daylight_Savings_Status); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision()); + break; + /* BACnet Legacy Support */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + apdu_len = encode_tagged_unsigned(&apdu[0], 1); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + if (Analog_Input_Count()) + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); + if (Analog_Output_Count()) + bitstring_set_bit(&bit_string, OBJECT_ANALOG_OUTPUT, true); + if (Analog_Value_Count()) + bitstring_set_bit(&bit_string, OBJECT_ANALOG_VALUE, true); + if (Binary_Input_Count()) + bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true); + if (Binary_Output_Count()) + bitstring_set_bit(&bit_string, OBJECT_BINARY_OUTPUT, true); + if (Binary_Value_Count()) + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + if (Life_Safety_Point_Count()) + bitstring_set_bit(&bit_string, OBJECT_LIFE_SAFETY_POINT, true); + if (Load_Control_Count()) + bitstring_set_bit(&bit_string, OBJECT_LOAD_CONTROL, true); + if (Multistate_Output_Count()) + bitstring_set_bit(&bit_string, OBJECT_MULTI_STATE_OUTPUT, + true); +#if BACFILE + if (bacfile_count()) + bitstring_set_bit(&bit_string, OBJECT_FILE, true); +#endif + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_tagged_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + /* reject message */ + apdu_len = -2; + break; + } + } else { + /* error: internal error? */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + apdu_len = -1; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_tagged_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_tagged_unsigned(&apdu[0], + Device_Max_APDU_Length_Accepted()); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = encode_tagged_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_tagged_unsigned(&apdu[0], APDU_Timeout); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = + encode_tagged_unsigned(&apdu[0], Number_Of_APDU_Retries); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = encode_tagged_unsigned(&apdu[0], Database_Revision); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type.Object_Id. + instance))) { + /* FIXME: we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_NUMBER_OF_APDU_RETRIES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* FIXME: bounds check? */ + Device_Set_Number_Of_APDU_Retries((uint8_t) value. + type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_APDU_TIMEOUT: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* FIXME: bounds check? */ + Device_Set_APDU_Timeout((uint16_t) value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_VENDOR_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* FIXME: bounds check? */ + Device_Set_Vendor_Identifier((uint16_t) value.type. + Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_SYSTEM_STATUS: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + Device_Set_System_Status(value.type.Enumerated); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + encoding = + characterstring_encoding(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + status = + Device_Set_Object_Name(characterstring_value(&value. + type.Character_String), + characterstring_length(&value.type.Character_String)); + if (!status) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testDevice(Test * pTest) +{ + bool status = false; + const char *name = "Patricia"; + + status = Device_Set_Object_Instance_Number(0); + ct_test(pTest, Device_Object_Instance_Number() == 0); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + ct_test(pTest, Device_Object_Instance_Number() == BACNET_MAX_INSTANCE); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE / 2); + ct_test(pTest, + Device_Object_Instance_Number() == (BACNET_MAX_INSTANCE / 2)); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE + 1); + ct_test(pTest, + Device_Object_Instance_Number() != (BACNET_MAX_INSTANCE + 1)); + ct_test(pTest, status == false); + + + Device_Set_System_Status(STATUS_NON_OPERATIONAL); + ct_test(pTest, Device_System_Status() == STATUS_NON_OPERATIONAL); + + Device_Set_Vendor_Name(name, strlen(name)); + ct_test(pTest, strcmp(Device_Vendor_Name(), name) == 0); + + Device_Set_Vendor_Identifier(42); + ct_test(pTest, Device_Vendor_Identifier() == 42); + + Device_Set_Model_Name(name, strlen(name)); + ct_test(pTest, strcmp(Device_Model_Name(), name) == 0); + + return; +} + +#ifdef TEST_DEVICE +/* stubs to dependencies to keep unit test simple */ +char *Analog_Input_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Analog_Input_Count(void) +{ + return 0; +} + +uint32_t Analog_Input_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Analog_Output_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Analog_Output_Count(void) +{ + return 0; +} + +uint32_t Analog_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Analog_Value_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Analog_Value_Count(void) +{ + return 0; +} + +uint32_t Analog_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Binary_Input_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Binary_Input_Count(void) +{ + return 0; +} + +uint32_t Binary_Input_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Binary_Output_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Binary_Output_Count(void) +{ + return 0; +} + +uint32_t Binary_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Binary_Value_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Binary_Value_Count(void) +{ + return 0; +} + +uint32_t Binary_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Life_Safety_Point_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Life_Safety_Point_Count(void) +{ + return 0; +} + +uint32_t Life_Safety_Point_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Load_Control_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Load_Control_Count(void) +{ + return 0; +} + +uint32_t Load_Control_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Multistate_Output_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Multistate_Output_Count(void) +{ + return 0; +} + +uint32_t Multistate_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +uint32_t bacfile_count(void) +{ + return 0; +} + +uint32_t bacfile_index_to_instance(unsigned find_index) +{ + return find_index; +} + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Device", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DEVICE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/device.h b/bacnet-stack-0-3-0/demo/object/device.h new file mode 100644 index 00000000..c59bac37 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/device.h @@ -0,0 +1,110 @@ +/*####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 DEVICE_H +#define DEVICE_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + uint32_t Device_Object_Instance_Number(void); + bool Device_Set_Object_Instance_Number(uint32_t object_id); + bool Device_Valid_Object_Instance_Number(uint32_t object_id); + unsigned Device_Object_List_Count(void); + bool Device_Object_List_Identifier(unsigned array_index, + int *object_type, uint32_t * instance); + + BACNET_DEVICE_STATUS Device_System_Status(void); + void Device_Set_System_Status(BACNET_DEVICE_STATUS status); + + const char *Device_Vendor_Name(void); + bool Device_Set_Vendor_Name(const char *name, size_t length); + + uint16_t Device_Vendor_Identifier(void); + void Device_Set_Vendor_Identifier(uint16_t vendor_id); + + const char *Device_Model_Name(void); + bool Device_Set_Model_Name(const char *name, size_t length); + + const char *Device_Firmware_Revision(void); + bool Device_Set_Firmware_Revision(const char *name, size_t length); + + const char *Device_Application_Software_Version(void); + bool Device_Set_Application_Software_Version(const char *name, + size_t length); + + const char *Device_Description(void); + bool Device_Set_Description(const char *name, size_t length); + + const char *Device_Location(void); + bool Device_Set_Location(const char *name, size_t length); + +/* some stack-centric constant values - no set methods */ + uint8_t Device_Protocol_Version(void); + uint8_t Device_Protocol_Revision(void); + uint16_t Device_Max_APDU_Length_Accepted(void); + BACNET_SEGMENTATION Device_Segmentation_Supported(void); + + uint16_t Device_APDU_Timeout(void); + void Device_Set_APDU_Timeout(uint16_t timeout); + + uint8_t Device_Number_Of_APDU_Retries(void); + void Device_Set_Number_Of_APDU_Retries(uint8_t retries); + + uint8_t Device_Database_Revision(void); + void Device_Set_Database_Revision(uint8_t revision); + + bool Device_Valid_Object_Name(const char *object_name, + int *object_type, uint32_t * object_instance); + char *Device_Valid_Object_Id(int object_type, + uint32_t object_instance); + + int Device_Encode_Property_APDU(uint8_t * apdu, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + + bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/device.mak b/bacnet-stack-0-3-0/demo/object/device.mak new file mode 100644 index 00000000..ca3893dc --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/device.mak @@ -0,0 +1,40 @@ +#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 -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + datetime.c \ + bacstr.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + apdu.c \ + dcc.c \ + demo/object/device.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = device + +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 diff --git a/bacnet-stack-0-3-0/demo/object/lc.c b/bacnet-stack-0-3-0/demo/object/lc.c new file mode 100644 index 00000000..2446286e --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lc.c @@ -0,0 +1,1016 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + +/* Load Control Objects - customize for your use */ + +#include +#include +#include +#include /* for memcpy */ +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "datetime.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "lc.h" +#include "ao.h" +#include "wp.h" + +/* number of demo objects */ +#define MAX_LOAD_CONTROLS 4 + +/* indicates the current load shedding state of the object */ +static BACNET_SHED_STATE Present_Value[MAX_LOAD_CONTROLS]; + +/* load control objects are required to support LEVEL */ +typedef enum BACnetShedLevelType { + BACNET_SHED_TYPE_PERCENT, /* Unsigned */ + BACNET_SHED_TYPE_LEVEL, /* Unsigned */ + BACNET_SHED_TYPE_AMOUNT /* REAL */ +} BACNET_SHED_LEVEL_TYPE; + +#define DEFAULT_VALUE_PERCENT 100 +#define DEFAULT_VALUE_LEVEL 0 +#define DEFAULT_VALUE_AMOUNT 0 + +/* The shed levels for the LEVEL choice of BACnetShedLevel + that have meaning for this particular Load Control object. */ +typedef struct { + BACNET_SHED_LEVEL_TYPE type; + union { + unsigned level; + unsigned percent; + float amount; + } value; +} BACNET_SHED_LEVEL; +/* indicates the desired load shedding */ +static BACNET_SHED_LEVEL Requested_Shed_Level[MAX_LOAD_CONTROLS]; +/* Indicates the amount of power that the object expects + to be able to shed in response to a load shed request. */ +static BACNET_SHED_LEVEL Expected_Shed_Level[MAX_LOAD_CONTROLS]; +/* Indicates the actual amount of power being shed in response + to a load shed request. */ +static BACNET_SHED_LEVEL Actual_Shed_Level[MAX_LOAD_CONTROLS]; + +/* indicates the start of the duty window in which the load controlled + by the Load Control object must be compliant with the requested shed. */ +static BACNET_DATE_TIME Start_Time[MAX_LOAD_CONTROLS]; +static BACNET_DATE_TIME End_Time[MAX_LOAD_CONTROLS]; +static BACNET_DATE_TIME Current_Time; + +/* indicates the duration of the load shed action, + starting at Start_Time in minutes */ +static uint32_t Shed_Duration[MAX_LOAD_CONTROLS]; + +/* indicates the time window used for load shed accounting in minutes */ +static uint32_t Duty_Window[MAX_LOAD_CONTROLS]; + +/* indicates and controls whether the Load Control object is + currently enabled to respond to load shed requests. */ +static bool Load_Control_Enable[MAX_LOAD_CONTROLS]; + +/* indicates when the object receives a write to any of the properties + Requested_Shed_Level, Shed_Duration, Duty_Window */ +static bool Load_Control_Request_Written[MAX_LOAD_CONTROLS]; +/* indicates when the object receives a write to Start_Time */ +static bool Start_Time_Property_Written[MAX_LOAD_CONTROLS]; + +/* optional: indicates the baseline power consumption value + for the sheddable load controlled by this object, + if a fixed baseline is used. + The units of Full_Duty_Baseline are kilowatts.*/ +static float Full_Duty_Baseline[MAX_LOAD_CONTROLS]; + +#define MAX_SHED_LEVELS 3 +/* Represents the shed levels for the LEVEL choice of + BACnetShedLevel that have meaning for this particular + Load Control object. */ +static unsigned Shed_Levels[MAX_LOAD_CONTROLS][MAX_SHED_LEVELS]; + +/* represents a description of the shed levels that the + Load Control object can take on. It is the same for + all the load control objects in this example device. */ +static char *Shed_Level_Descriptions[MAX_SHED_LEVELS] = { + "dim lights 10%", + "dim lights 20%", + "dim lights 30%" +}; +static float Shed_Level_Values[MAX_SHED_LEVELS] = { + 90.0, + 80.0, + 70.0 +}; + + +/* we need to have our arrays initialized before answering any calls */ +static bool Load_Control_Initialized = false; + +void Load_Control_Init(void) +{ + unsigned i, j; + + if (!Load_Control_Initialized) { + Load_Control_Initialized = true; + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + /* FIXME: load saved data? */ + Present_Value[i] = BACNET_SHED_INACTIVE; + Requested_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Requested_Shed_Level[i].value.level = 0; + datetime_wildcard_set(&Start_Time[i]); + Shed_Duration[i] = 0; + Duty_Window[i] = 0; + Load_Control_Enable[i] = true; + Full_Duty_Baseline[i] = 1.500; /* kilowatts */ + for (j = 0; j < MAX_SHED_LEVELS; j++) { + /* FIXME: fake data for lighting application */ + /* The array shall be ordered by increasing shed amount. */ + Shed_Levels[i][j] = 1 + j; + } + Expected_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Expected_Shed_Level[i].value.level = 0; + Actual_Shed_Level[i].type = BACNET_SHED_TYPE_LEVEL; + Actual_Shed_Level[i].value.level = 0; + Load_Control_Request_Written[i] = false; + Start_Time_Property_Written[i] = false; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Load_Control_Valid_Instance(uint32_t object_instance) +{ + Load_Control_Init(); + if (object_instance < MAX_LOAD_CONTROLS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Load_Control_Count(void) +{ + Load_Control_Init(); + return MAX_LOAD_CONTROLS; +} + +/* 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 Load_Control_Index_To_Instance(unsigned index) +{ + Load_Control_Init(); + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Load_Control_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_LOAD_CONTROLS; + + Load_Control_Init(); + if (object_instance < MAX_LOAD_CONTROLS) + index = object_instance; + + return index; +} + +static BACNET_SHED_STATE Load_Control_Present_Value(uint32_t + object_instance) +{ + BACNET_SHED_STATE value = BACNET_SHED_INACTIVE; + unsigned index = 0; + + Load_Control_Init(); + index = Load_Control_Instance_To_Index(object_instance); + if (index < MAX_LOAD_CONTROLS) { + value = Present_Value[index]; + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Load_Control_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_LOAD_CONTROLS) { + sprintf(text_string, "LOAD CONTROL %u", object_instance); + return text_string; + } + + return NULL; +} + +static void Update_Current_Time(BACNET_DATE_TIME * bdatetime) +{ + time_t timer; + struct tm *tblock; + +/* +struct tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; +}; +*/ + + timer = time(NULL); + tblock = localtime(&timer); + datetime_set_values(bdatetime, + (uint16_t) tblock->tm_year, + (uint8_t) tblock->tm_mon, + (uint8_t) tblock->tm_mday, + (uint8_t) tblock->tm_hour, + (uint8_t) tblock->tm_min, (uint8_t) tblock->tm_sec, 0); +} + +/* convert the shed level request into an Analog Output Present_Value */ +static float Requested_Shed_Level_Value(int object_index) +{ + unsigned shed_level_index = 0; + unsigned i = 0; + float requested_level = 0.0; + + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + requested_level = + (float) Requested_Shed_Level[object_index].value.percent; + break; + case BACNET_SHED_TYPE_AMOUNT: + /* Assumptions: wattage is linear with analog output level */ + requested_level = + Full_Duty_Baseline[object_index] - + Requested_Shed_Level[object_index].value.amount; + requested_level /= Full_Duty_Baseline[object_index]; + requested_level *= 100.0; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + for (i = 0; i < MAX_SHED_LEVELS; i++) { + if (Shed_Levels[object_index][i] <= + Requested_Shed_Level[object_index].value.level) + shed_level_index = i; + } + requested_level = Shed_Level_Values[shed_level_index]; + break; + } + + return requested_level; +} + +static void Shed_Level_Copy(BACNET_SHED_LEVEL * dest, + BACNET_SHED_LEVEL * src) +{ + if (dest && src) { + dest->type = src->type; + switch (src->type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = src->value.percent; + break; + case BACNET_SHED_TYPE_AMOUNT: + dest->value.amount = src->value.amount; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + dest->value.level = src->value.level; + break; + } + } +} + +static void Shed_Level_Default_Set(BACNET_SHED_LEVEL * dest, + BACNET_SHED_LEVEL_TYPE type) +{ + if (dest) { + dest->type = type; + switch (type) { + case BACNET_SHED_TYPE_PERCENT: + dest->value.percent = 100; + break; + case BACNET_SHED_TYPE_AMOUNT: + dest->value.amount = 0.0; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + dest->value.level = 0; + break; + } + } +} + +static bool Able_To_Meet_Shed_Request(int object_index) +{ + float level = 0.0; + float requested_level = 0.0; + unsigned priority = 0; + bool status = false; + int object_instance = 0; + + /* This demo is going to use the Analog Outputs as their Load */ + object_instance = object_index; + priority = Analog_Output_Present_Value_Priority(object_instance); + /* we are controlling at Priority 4 - can we control the output? */ + if (priority >= 4) { + /* is the level able to be lowered? */ + requested_level = Requested_Shed_Level_Value(object_index); + level = Analog_Output_Present_Value(object_instance); + if (level >= requested_level) { + status = true; + } + } + + return status; +} + +typedef enum load_control_state { + SHED_INACTIVE, + SHED_REQUEST_PENDING, + SHED_NON_COMPLIANT, + SHED_COMPLIANT, + MAX_LOAD_CONTROL_STATE +} LOAD_CONTROL_STATE; +static LOAD_CONTROL_STATE Load_Control_State[MAX_LOAD_CONTROLS]; +static LOAD_CONTROL_STATE Load_Control_State_Previously[MAX_LOAD_CONTROLS]; + +static void Print_Load_Control_State(int object_index) +{ + char *Load_Control_State_Text[MAX_LOAD_CONTROLS] = { + "SHED_INACTIVE", + "SHED_REQUEST_PENDING", + "SHED_NON_COMPLIANT", + "SHED_COMPLIANT" + }; + + if (object_index < MAX_LOAD_CONTROLS) { + if (Load_Control_State[object_index] < MAX_LOAD_CONTROL_STATE) { + printf("Load Control[%d]=%s\n", object_index, + Load_Control_State_Text[Load_Control_State[object_index]]); + } + } +} + +void Load_Control_State_Machine(int object_index) +{ + unsigned i = 0; /* loop counter */ + int diff = 0; /* used for datetime comparison */ + + switch (Load_Control_State[object_index]) { + case SHED_REQUEST_PENDING: + if (Load_Control_Request_Written[object_index]) { + Load_Control_Request_Written[object_index] = false; + /* request to cancel using default values? */ + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + if (Requested_Shed_Level[object_index].value.percent == + DEFAULT_VALUE_PERCENT) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + case BACNET_SHED_TYPE_AMOUNT: + if (Requested_Shed_Level[object_index].value.amount == + DEFAULT_VALUE_AMOUNT) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + case BACNET_SHED_TYPE_LEVEL: + default: + if (Requested_Shed_Level[object_index].value.level == + DEFAULT_VALUE_LEVEL) + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_State[object_index] == SHED_INACTIVE) { + printf("Load Control[%d]:Requested Shed Level=Default\n", + object_index); + break; + } + } + if (Start_Time_Property_Written[object_index]) { + Start_Time_Property_Written[object_index] = false; + /* request to cancel using wildcards in start time? */ + if (datetime_wildcard(&Start_Time[object_index])) { + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + } + /* cancel because current time is after start time + duration? */ + Update_Current_Time(&Current_Time); + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* CancelShed */ + /* FIXME: stop shedding! i.e. relinquish */ + printf + ("Load Control[%d]:Current Time is after Start Time + Duration\n", + object_index); + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + diff = datetime_compare(&Current_Time, &Start_Time[object_index]); + if (diff < 0) { + /* current time prior to start time */ + /* ReconfigurePending */ + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + } else if (diff > 0) { + /* current time after to start time */ + printf("Load Control[%d]:Current Time is after Start Time\n", + object_index); + /* AbleToMeetShed */ + if (Able_To_Meet_Shed_Request(object_index)) { + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy(&Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Load_Control_State[object_index] = SHED_COMPLIANT; + } else { + /* CannotMeetShed */ + Shed_Level_Default_Set(&Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_NON_COMPLIANT; + } + } + break; + case SHED_NON_COMPLIANT: + Update_Current_Time(&Current_Time); + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* FinishedUnsuccessfulShed */ + printf + ("Load Control[%d]:Current Time is after Start Time + Duration\n", + object_index); + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_Request_Written[object_index] || + Start_Time_Property_Written[object_index]) { + /* UnsuccessfulShedReconfigured */ + printf("Load Control[%d]:Control Property written\n", + object_index); + Load_Control_Request_Written[object_index] = false; + Start_Time_Property_Written[object_index] = false; + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + break; + } + if (Able_To_Meet_Shed_Request(object_index)) { + /* CanNowComplyWithShed */ + printf("Load Control[%d]:Able to meet Shed Request\n", + object_index); + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Analog_Output_Present_Value_Set(object_index, + Requested_Shed_Level_Value(object_index), 4); + Shed_Level_Copy(&Actual_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Load_Control_State[object_index] = SHED_COMPLIANT; + } + break; + case SHED_COMPLIANT: + Update_Current_Time(&Current_Time); + datetime_copy(&End_Time[object_index], &Start_Time[object_index]); + datetime_add_minutes(&End_Time[object_index], + Shed_Duration[object_index]); + diff = datetime_compare(&End_Time[object_index], &Current_Time); + if (diff < 0) { + /* FinishedSuccessfulShed */ + printf + ("Load Control[%d]:Current Time is after Start Time + Duration\n", + object_index); + datetime_wildcard_set(&Start_Time[i]); + Load_Control_State[object_index] = SHED_INACTIVE; + break; + } + if (Load_Control_Request_Written[object_index] || + Start_Time_Property_Written[object_index]) { + /* UnsuccessfulShedReconfigured */ + printf("Load Control[%d]:Control Property written\n", + object_index); + Load_Control_Request_Written[object_index] = false; + Start_Time_Property_Written[object_index] = false; + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + break; + } + if (!Able_To_Meet_Shed_Request(object_index)) { + /* CanNoLongerComplyWithShed */ + printf("Load Control[%d]:Not able to meet Shed Request\n", + object_index); + Shed_Level_Default_Set(&Expected_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_NON_COMPLIANT; + } + break; + case SHED_INACTIVE: + default: + if (Start_Time_Property_Written[object_index]) { + printf("Load Control[%d]:Start Time written\n", object_index); + Start_Time_Property_Written[object_index] = false; + Shed_Level_Copy(&Expected_Shed_Level[object_index], + &Requested_Shed_Level[object_index]); + Shed_Level_Default_Set(&Actual_Shed_Level[object_index], + Requested_Shed_Level[object_index].type); + Load_Control_State[object_index] = SHED_REQUEST_PENDING; + } + break; + } + + return; +} + +/* call every second or so */ +void Load_Control_State_Machine_Handler(void) +{ + unsigned i = 0; + static bool initialized = false; + + Load_Control_Init(); + if (!initialized) { + initialized = true; + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + Load_Control_State[i] = SHED_INACTIVE; + Load_Control_State_Previously[i] = SHED_INACTIVE; + } + } + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + Load_Control_State_Machine(i); + if (Load_Control_State[i] != Load_Control_State_Previously[i]) { + Print_Load_Control_State(i); + Load_Control_State_Previously[i] = Load_Control_State[i]; + } + + + } +} + +/* return apdu len, or -1 on error */ +int Load_Control_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + int enumeration = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Load_Control_Init(); + object_index = Load_Control_Instance_To_Index(object_instance); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_LOAD_CONTROL, + object_instance); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, + Load_Control_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_LOAD_CONTROL); + break; + /* optional property + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string,"optional description"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + */ + case PROP_PRESENT_VALUE: + enumeration = Load_Control_Present_Value(object_instance); + apdu_len = encode_tagged_enumerated(&apdu[0], enumeration); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + /* IN_ALARM - Logical FALSE (0) if the Event_State property + has a value of NORMAL, otherwise logical TRUE (1). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + /* FAULT - Logical TRUE (1) if the Reliability property is + present and does not have a value of NO_FAULT_DETECTED, + otherwise logical FALSE (0). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + /* OVERRIDDEN - Logical TRUE (1) if the point has been + overridden by some mechanism local to the BACnet Device, + otherwise logical FALSE (0). */ + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + /* OUT_OF_SERVICE - This bit shall always be Logical FALSE (0). */ + 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_REQUESTED_SHED_LEVEL: + switch (Requested_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = encode_context_unsigned(&apdu[0], 0, + Requested_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = encode_context_real(&apdu[0], 2, + Requested_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = encode_context_unsigned(&apdu[0], 1, + Requested_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_START_TIME: + len = encode_tagged_date(&apdu[0], &Start_Time[object_index].date); + apdu_len = len; + len = encode_tagged_time(&apdu[apdu_len], + &Start_Time[object_index].time); + apdu_len += len; + break; + case PROP_SHED_DURATION: + apdu_len = encode_tagged_unsigned(&apdu[0], + Shed_Duration[object_index]); + break; + case PROP_DUTY_WINDOW: + apdu_len = encode_tagged_unsigned(&apdu[0], + Duty_Window[object_index]); + break; + case PROP_ENABLE: + state = Load_Control_Enable[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_FULL_DUTY_BASELINE: /* optional */ + apdu_len = encode_tagged_real(&apdu[0], + Full_Duty_Baseline[object_index]); + break; + case PROP_EXPECTED_SHED_LEVEL: + switch (Expected_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = encode_context_unsigned(&apdu[0], 0, + Expected_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = encode_context_real(&apdu[0], 2, + Expected_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = encode_context_unsigned(&apdu[0], 1, + Expected_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_ACTUAL_SHED_LEVEL: + switch (Actual_Shed_Level[object_index].type) { + case BACNET_SHED_TYPE_PERCENT: + apdu_len = encode_context_unsigned(&apdu[0], 0, + Actual_Shed_Level[object_index].value.percent); + break; + case BACNET_SHED_TYPE_AMOUNT: + apdu_len = encode_context_real(&apdu[0], 2, + Actual_Shed_Level[object_index].value.amount); + break; + case BACNET_SHED_TYPE_LEVEL: + default: + apdu_len = encode_context_unsigned(&apdu[0], 1, + Actual_Shed_Level[object_index].value.level); + break; + } + break; + case PROP_SHED_LEVELS: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], MAX_SHED_LEVELS); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + apdu_len = 0; + for (i = 0; i < MAX_SHED_LEVELS; i++) { + /* FIXME: check if we have room before adding it to APDU */ + len = + encode_tagged_unsigned(&apdu[apdu_len], + Shed_Levels[object_index][i]); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + if (array_index <= MAX_SHED_LEVELS) { + apdu_len = encode_tagged_unsigned(&apdu[0], + Shed_Levels[object_index][array_index - 1]); + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + case PROP_SHED_LEVEL_DESCRIPTIONS: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], MAX_SHED_LEVELS); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + apdu_len = 0; + for (i = 0; i < MAX_SHED_LEVELS; i++) { + /* FIXME: check if we have room before adding it to APDU */ + characterstring_init_ansi(&char_string, + Shed_Level_Descriptions[i]); + len = encode_tagged_character_string(&apdu[apdu_len], + &char_string); + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + if (array_index <= MAX_SHED_LEVELS) { + characterstring_init_ansi(&char_string, + Shed_Level_Descriptions[array_index - 1]); + apdu_len = encode_tagged_character_string(&apdu[0], + &char_string); + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Load_Control_Init(); + if (!Load_Control_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + object_index = + Load_Control_Instance_To_Index(wp_data->object_instance); + switch (wp_data->object_property) { + case PROP_REQUESTED_SHED_LEVEL: + len = bacapp_decode_context_data(wp_data->application_data, + wp_data->application_data_len, + &value, PROP_REQUESTED_SHED_LEVEL); + if (value.tag == 0) { + /* percent - Unsigned */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_PERCENT; + Requested_Shed_Level[object_index].value.percent = + value.type.Unsigned_Int; + status = true; + } else if (value.tag == 1) { + /* level - Unsigned */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_LEVEL; + Requested_Shed_Level[object_index].value.level = + value.type.Unsigned_Int; + status = true; + } else if (value.tag == 2) { + /* amount - REAL */ + Requested_Shed_Level[object_index].type = + BACNET_SHED_TYPE_AMOUNT; + Requested_Shed_Level[object_index].value.amount = + value.type.Real; + status = true; + } else { + /* error! */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + if (status) { + Load_Control_Request_Written[object_index] = true; + } + break; + case PROP_START_TIME: + if (value.tag == BACNET_APPLICATION_TAG_DATE) { + memcpy(&Start_Time[object_index].date, + &value.type.Date, sizeof(value.type.Date)); + Start_Time_Property_Written[object_index] = true; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + if (!status) + break; + len = + bacapp_decode_application_data(wp_data->application_data + len, + wp_data->application_data_len - len, &value); + if (len && value.tag == BACNET_APPLICATION_TAG_TIME) { + memcpy(&Start_Time[object_index].time, + &value.type.Time, sizeof(value.type.Time)); + status = true; + } else { + status = false; + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_SHED_DURATION: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + Shed_Duration[object_index] = value.type.Unsigned_Int; + Load_Control_Request_Written[object_index] = true; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_DUTY_WINDOW: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + Duty_Window[object_index] = value.type.Unsigned_Int; + Load_Control_Request_Written[object_index] = true; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_SHED_LEVELS: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + /* re-write the size of the array? */ + if (wp_data->array_index == 0) { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (wp_data->array_index == BACNET_ARRAY_ALL) { + /* FIXME: write entire array */ + status = true; + } else if (wp_data->array_index <= MAX_SHED_LEVELS) { + Shed_Levels[object_index][wp_data->array_index - 1] = + value.type.Unsigned_Int; + status = true; + } else { + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_ENABLE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + Load_Control_Enable[object_index] = value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testLoadControlStateMachine(Test * pTest) +{ + unsigned i = 0, j = 0; + + Load_Control_Init(); + /* validate the triggers for each state change */ + for (j = 0; j < 20; j++) { + Load_Control_State_Machine_Handler(); + for (i = 0; i < MAX_LOAD_CONTROLS; i++) { + ct_test(pTest, Load_Control_State[i] == SHED_INACTIVE); + } + } +} + +void testLoadControl(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_LOAD_CONTROL; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Load_Control_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_LOAD_CONTROL); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_LOAD_CONTROL +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Load Control", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLoadControl); + assert(rc); + rc = ct_addTestFunction(pTest, testLoadControlStateMachine); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_LOAD_CONTROL */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/lc.h b/bacnet-stack-0-3-0/demo/object/lc.h new file mode 100644 index 00000000..532c76da --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lc.h @@ -0,0 +1,62 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef LOADCONTROL_H +#define LOADCONTROL_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Load_Control_State_Machine_Handler(void); + + bool Load_Control_Valid_Instance(uint32_t object_instance); + unsigned Load_Control_Count(void); + uint32_t Load_Control_Index_To_Instance(unsigned index); + char *Load_Control_Name(uint32_t object_instance); + + int Load_Control_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); + + bool Load_Control_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testLoadControl(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/lc.ide b/bacnet-stack-0-3-0/demo/object/lc.ide new file mode 100644 index 00000000..682eafcb Binary files /dev/null and b/bacnet-stack-0-3-0/demo/object/lc.ide differ diff --git a/bacnet-stack-0-3-0/demo/object/lc.mak b/bacnet-stack-0-3-0/demo/object/lc.mak new file mode 100644 index 00000000..17290ef5 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lc.mak @@ -0,0 +1,39 @@ +#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_LOAD_CONTROL -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + datetime.c \ + demo/object/ao.c \ + demo/object/lc.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = loadcontrol + +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 diff --git a/bacnet-stack-0-3-0/demo/object/lsp.c b/bacnet-stack-0-3-0/demo/object/lsp.c new file mode 100644 index 00000000..703764f6 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lsp.c @@ -0,0 +1,356 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Life Safety Point Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_LIFE_SAFETY_POINTS 7 + +/* Here are our stored levels.*/ +static BACNET_LIFE_SAFETY_MODE + Life_Safety_Point_Mode[MAX_LIFE_SAFETY_POINTS]; +static BACNET_LIFE_SAFETY_STATE + Life_Safety_Point_State[MAX_LIFE_SAFETY_POINTS]; +static BACNET_SILENCED_STATE + Life_Safety_Point_Silenced_State[MAX_LIFE_SAFETY_POINTS]; +static BACNET_LIFE_SAFETY_OPERATION + Life_Safety_Point_Operation[MAX_LIFE_SAFETY_POINTS]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Life_Safety_Point_Out_Of_Service[MAX_LIFE_SAFETY_POINTS]; + +void Life_Safety_Point_Init(void) +{ + static bool initialized = false; + unsigned i; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_LIFE_SAFETY_POINTS; i++) { + Life_Safety_Point_Mode[i] = LIFE_SAFETY_MODE_DEFAULT; + Life_Safety_Point_State[i] = LIFE_SAFETY_STATE_QUIET; + Life_Safety_Point_Silenced_State[i] = + SILENCED_STATE_UNSILENCED; + Life_Safety_Point_Operation[i] = LIFE_SAFETY_OPERATION_NONE; + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Life_Safety_Point_Valid_Instance(uint32_t object_instance) +{ + Life_Safety_Point_Init(); + if (object_instance < MAX_LIFE_SAFETY_POINTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Life_Safety_Point_Count(void) +{ + Life_Safety_Point_Init(); + return MAX_LIFE_SAFETY_POINTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Life_Safety_Point_Index_To_Instance(unsigned index) +{ + Life_Safety_Point_Init(); + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Life_Safety_Point_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_LIFE_SAFETY_POINTS; + + Life_Safety_Point_Init(); + if (object_instance < MAX_LIFE_SAFETY_POINTS) + index = object_instance; + + return index; +} + +static BACNET_LIFE_SAFETY_STATE Life_Safety_Point_Present_Value(uint32_t + object_instance) +{ + BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; + unsigned index = 0; + + Life_Safety_Point_Init(); + index = Life_Safety_Point_Instance_To_Index(object_instance); + if (index < MAX_LIFE_SAFETY_POINTS) + present_value = Life_Safety_Point_State[index]; + + return present_value; +} + +/* note: the object name must be unique within this device */ +char *Life_Safety_Point_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_LIFE_SAFETY_POINTS) { + sprintf(text_string, "LS POINT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Life_Safety_Point_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; + BACNET_LIFE_SAFETY_MODE mode = LIFE_SAFETY_MODE_DEFAULT; + BACNET_SILENCED_STATE silenced_state = SILENCED_STATE_UNSILENCED; + BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OPERATION_NONE; + unsigned object_index = 0; + bool state = false; + BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; + (void) array_index; /* currently not used */ + Life_Safety_Point_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_tagged_object_id(&apdu[0], OBJECT_LIFE_SAFETY_POINT, + object_instance); + break; + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Life_Safety_Point_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_LIFE_SAFETY_POINT); + break; + case PROP_PRESENT_VALUE: + present_value = Life_Safety_Point_Present_Value(object_instance); + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); + apdu_len = encode_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: + object_index = + Life_Safety_Point_Instance_To_Index(object_instance); + state = Life_Safety_Point_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_RELIABILITY: + /* see standard for details about this property */ + reliability = RELIABILITY_NO_FAULT_DETECTED; + apdu_len = encode_tagged_enumerated(&apdu[0], reliability); + break; + case PROP_MODE: + object_index = + Life_Safety_Point_Instance_To_Index(object_instance); + mode = Life_Safety_Point_Mode[object_index]; + apdu_len = encode_tagged_enumerated(&apdu[0], mode); + break; + case PROP_ACCEPTED_MODES: + for (mode = MIN_LIFE_SAFETY_MODE; mode < MAX_LIFE_SAFETY_MODE; + mode++) { + len = encode_tagged_enumerated(&apdu[apdu_len], mode); + apdu_len += len; + } + break; + case PROP_SILENCED: + object_index = + Life_Safety_Point_Instance_To_Index(object_instance); + silenced_state = Life_Safety_Point_Silenced_State[object_index]; + apdu_len = encode_tagged_enumerated(&apdu[0], silenced_state); + break; + case PROP_OPERATION_EXPECTED: + object_index = + Life_Safety_Point_Instance_To_Index(object_instance); + operation = Life_Safety_Point_Operation[object_index]; + apdu_len = encode_tagged_enumerated(&apdu[0], operation); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Life_Safety_Point_Init(); + if (!Life_Safety_Point_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_MODE: + if (value.tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if ((value.type.Enumerated >= MIN_LIFE_SAFETY_MODE) && + (value.type.Enumerated <= MIN_LIFE_SAFETY_MODE)) { + object_index = + Life_Safety_Point_Instance_To_Index(wp_data-> + object_instance); + Life_Safety_Point_Mode[object_index] = + value.type.Enumerated; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Life_Safety_Point_Instance_To_Index(wp_data-> + object_instance); + Life_Safety_Point_Out_Of_Service[object_index] = + value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testLifeSafetyPoint(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_LIFE_SAFETY_POINT; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Life_Safety_Point_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_LIFE_SAFETY_POINT); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_LIFE_SAFETY_POINT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Life Safety Point", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testLifeSafetyPoint); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_LIFE_SAFETY_POINT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/lsp.h b/bacnet-stack-0-3-0/demo/object/lsp.h new file mode 100644 index 00000000..4c3ef01f --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lsp.h @@ -0,0 +1,61 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef LSP_H +#define LSP_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Life_Safety_Point_Valid_Instance(uint32_t object_instance); + unsigned Life_Safety_Point_Count(void); + uint32_t Life_Safety_Point_Index_To_Instance(unsigned index); + char *Life_Safety_Point_Name(uint32_t object_instance); + + int Life_Safety_Point_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); + + bool Life_Safety_Point_Write_Property(BACNET_WRITE_PROPERTY_DATA * + wp_data, BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testLifeSafetyPoint(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/lsp.mak b/bacnet-stack-0-3-0/demo/object/lsp.mak new file mode 100755 index 00000000..1210a96c --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/lsp.mak @@ -0,0 +1,38 @@ +#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_LIFE_SAFETY_POINT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/lsp.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = lsp + +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 diff --git a/bacnet-stack-0-3-0/demo/object/mso.c b/bacnet-stack-0-3-0/demo/object/mso.c new file mode 100644 index 00000000..4c0395e9 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/mso.c @@ -0,0 +1,433 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* Multi-state Output Objects - customize for your use */ + +#include +#include +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" /* the custom stuff */ +#include "wp.h" + +#define MAX_MULTISTATE_OUTPUTS 4 + +/* When all the priorities are level null, the present value returns */ +/* the Relinquish Default value */ +#define MULTISTATE_RELINQUISH_DEFAULT 0 + +/* NULL part of the array */ +#define MULTISTATE_NULL (255) +/* how many states? 0-253 is 254 states */ +#define MULTISTATE_NUMBER_OF_STATES (254) +/* Here is our Priority Array.*/ +static uint8_t + Multistate_Output_Level[MAX_MULTISTATE_OUTPUTS][BACNET_MAX_PRIORITY]; +/* Writable out-of-service allows others to play with our Present Value */ +/* without changing the physical output */ +static bool Multistate_Output_Out_Of_Service[MAX_MULTISTATE_OUTPUTS]; + +void Multistate_Output_Init(void) +{ + unsigned i, j; + static bool initialized = false; + + if (!initialized) { + initialized = true; + + /* initialize all the analog output priority arrays to NULL */ + for (i = 0; i < MAX_MULTISTATE_OUTPUTS; i++) { + for (j = 0; j < BACNET_MAX_PRIORITY; j++) { + Multistate_Output_Level[i][j] = MULTISTATE_NULL; + } + } + } + + return; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Multistate_Output_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_MULTISTATE_OUTPUTS) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Multistate_Output_Count(void) +{ + return MAX_MULTISTATE_OUTPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Multistate_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Multistate_Output_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_MULTISTATE_OUTPUTS; + + if (object_instance < MAX_MULTISTATE_OUTPUTS) + index = object_instance; + + return index; +} + +static uint32_t Multistate_Output_Present_Value(uint32_t object_instance) +{ + uint32_t value = MULTISTATE_RELINQUISH_DEFAULT; + unsigned index = 0; + unsigned i = 0; + + Multistate_Output_Init(); + index = Multistate_Output_Instance_To_Index(object_instance); + if (index < MAX_MULTISTATE_OUTPUTS) { + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + if (Multistate_Output_Level[index][i] != MULTISTATE_NULL) { + value = Multistate_Output_Level[index][i]; + break; + } + } + } + + return value; +} + +/* note: the object name must be unique within this device */ +char *Multistate_Output_Name(uint32_t object_instance) +{ + static char text_string[32] = ""; /* okay for single thread */ + + if (object_instance < MAX_MULTISTATE_OUTPUTS) { + sprintf(text_string, "MULTISTATE OUTPUT %u", object_instance); + return text_string; + } + + return NULL; +} + +/* return apdu len, or -1 on error */ +int Multistate_Output_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 len = 0; + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + uint32_t present_value = 0; + unsigned object_index = 0; + unsigned i = 0; + bool state = false; + + Multistate_Output_Init(); + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_tagged_object_id(&apdu[0], OBJECT_MULTI_STATE_OUTPUT, + object_instance); + break; + /* note: Name and Description don't have to be the same. + You could make Description writable and different */ + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, + Multistate_Output_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_MULTI_STATE_OUTPUT); + break; + case PROP_PRESENT_VALUE: + present_value = Multistate_Output_Present_Value(object_instance); + apdu_len = encode_tagged_unsigned(&apdu[0], present_value); + break; + case PROP_STATUS_FLAGS: + /* note: see the details in the standard on how to use these */ + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + 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: + /* note: see the details in the standard on how to use this */ + apdu_len = encode_tagged_enumerated(&apdu[0], EVENT_STATE_NORMAL); + break; + case PROP_OUT_OF_SERVICE: + object_index = + Multistate_Output_Instance_To_Index(object_instance); + state = Multistate_Output_Out_Of_Service[object_index]; + apdu_len = encode_tagged_boolean(&apdu[0], state); + break; + case PROP_PRIORITY_ARRAY: + /* Array element zero is the number of elements in the array */ + if (array_index == 0) + apdu_len = + encode_tagged_unsigned(&apdu[0], BACNET_MAX_PRIORITY); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (array_index == BACNET_ARRAY_ALL) { + object_index = + Multistate_Output_Instance_To_Index(object_instance); + for (i = 0; i < BACNET_MAX_PRIORITY; i++) { + /* FIXME: check if we have room before adding it to APDU */ + if (Multistate_Output_Level[object_index][i] == + MULTISTATE_NULL) + len = encode_tagged_null(&apdu[apdu_len]); + else { + present_value = + Multistate_Output_Level[object_index][i]; + len = + encode_tagged_unsigned(&apdu[apdu_len], + present_value); + } + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } + } else { + object_index = + Multistate_Output_Instance_To_Index(object_instance); + if (array_index <= BACNET_MAX_PRIORITY) { + if (Multistate_Output_Level[object_index][array_index - + 1] == MULTISTATE_NULL) + apdu_len = encode_tagged_null(&apdu[0]); + else { + present_value = + Multistate_Output_Level[object_index][array_index - + 1]; + apdu_len = + encode_tagged_unsigned(&apdu[0], present_value); + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + + break; + case PROP_RELINQUISH_DEFAULT: + present_value = MULTISTATE_RELINQUISH_DEFAULT; + apdu_len = encode_tagged_enumerated(&apdu[0], present_value); + break; + case PROP_NUMBER_OF_STATES: + apdu_len = encode_tagged_unsigned(&apdu[apdu_len], + MULTISTATE_NUMBER_OF_STATES); + break; + + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + unsigned int priority = 0; + uint32_t level = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + Multistate_Output_Init(); + if (!Multistate_Output_Valid_Instance(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + priority = wp_data->priority; + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (priority && (priority <= BACNET_MAX_PRIORITY) && + (priority != 6 /* reserved */ ) && + (value.type.Unsigned_Int <= MULTISTATE_NUMBER_OF_STATES)) { + level = value.type.Unsigned_Int; + object_index = + Multistate_Output_Instance_To_Index(wp_data-> + object_instance); + priority--; + Multistate_Output_Level[object_index][priority] = + (uint8_t) level; + /* Note: you could set the physical output here if we + are the highest priority. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else if (priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else if (value.tag == BACNET_APPLICATION_TAG_NULL) { + level = MULTISTATE_NULL; + object_index = + Multistate_Output_Instance_To_Index(wp_data-> + object_instance); + priority = wp_data->priority; + if (priority && (priority <= BACNET_MAX_PRIORITY)) { + priority--; + Multistate_Output_Level[object_index][priority] = + (uint8_t) level; + /* Note: you could set the physical output here to the next + highest priority, or to the relinquish default if no + priorities are set. + However, if Out of Service is TRUE, then don't set the + physical output. This comment may apply to the + main loop (i.e. check out of service before changing output) */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OUT_OF_SERVICE: + if (value.tag == BACNET_APPLICATION_TAG_BOOLEAN) { + object_index = + Multistate_Output_Instance_To_Index(wp_data-> + object_instance); + Multistate_Output_Out_Of_Service[object_index] = + value.type.Boolean; + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testMultistateOutput(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_MULTI_STATE_OUTPUT; + uint32_t decoded_instance = 0; + uint32_t instance = 123; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + + len = Multistate_Output_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_MULTI_STATE_OUTPUT); + ct_test(pTest, decoded_instance == instance); + + return; +} + +#ifdef TEST_MULTISTATE_OUTPUT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Multi-state Output", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testMultistateOutput); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_BINARY_INPUT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/demo/object/mso.h b/bacnet-stack-0-3-0/demo/object/mso.h new file mode 100644 index 00000000..d617ea8b --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/mso.h @@ -0,0 +1,61 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef MSO_H +#define MSO_H + +#include +#include +#include "bacdef.h" +#include "bacerror.h" +#include "wp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Multistate_Output_Valid_Instance(uint32_t object_instance); + unsigned Multistate_Output_Count(void); + uint32_t Multistate_Output_Index_To_Instance(unsigned index); + char *Multistate_Output_Name(uint32_t object_instance); + + int Multistate_Output_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); + + bool Multistate_Output_Write_Property(BACNET_WRITE_PROPERTY_DATA * + wp_data, BACNET_ERROR_CLASS * error_class, + BACNET_ERROR_CODE * error_code); + +#ifdef TEST +#include "ctest.h" + void testMultistateOutput(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/demo/object/mso.mak b/bacnet-stack-0-3-0/demo/object/mso.mak new file mode 100644 index 00000000..971374c6 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/object/mso.mak @@ -0,0 +1,38 @@ +#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_MULTISTATE_OUTPUT -g + +# NOTE: this file is normally called by the unittest.sh from up directory +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + demo/object/mso.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = multistate_output + +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 diff --git a/bacnet-stack-0-3-0/demo/readfile/Makefile b/bacnet-stack-0-3-0/demo/readfile/Makefile new file mode 100644 index 00000000..ebe7772d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readfile/Makefile @@ -0,0 +1,83 @@ +#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 = bacarf + +SRCS = main.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)/s_whois.c \ + $(BACNET_HANDLER)/s_arfs.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_ROOT)/rp.c \ + $(BACNET_ROOT)/bacdcode.c \ + $(BACNET_ROOT)/bacapp.c \ + $(BACNET_ROOT)/bacprop.c \ + $(BACNET_ROOT)/bacstr.c \ + $(BACNET_ROOT)/bactext.c \ + $(BACNET_ROOT)/indtext.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/bigend.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/tsm.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/filename.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)/arf.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/readfile/main.c b/bacnet-stack-0-3-0/demo/readfile/main.c new file mode 100644 index 00000000..0b219ed2 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readfile/main.c @@ -0,0 +1,318 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#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" +/* 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_File_Object_Instance = BACNET_MAX_INSTANCE; +static uint32_t Target_Device_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_ADDRESS Target_Address; +static char *Local_File_Name = NULL; +static bool End_Of_File_Detected = false; +static bool Error_Detected = false; +static uint8_t Current_Invoke_ID = 0; + +static void Atomic_Read_File_Error_Handler(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("\r\nBACnet Error!\r\n"); + printf("Error Class: %s\r\n", bactext_error_class_name(error_class)); + printf("Error Code: %s\r\n", 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("\r\nBACnet Abort!\r\n"); + printf("Abort Reason: %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("\r\nBACnet Reject!\r\n"); + printf("Reject Reason: %s\r\n", + bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void AtomicReadFileAckHandler(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; /* stream pointer */ + size_t octets_written = 0; + + (void) src; /* FIXME: validate the source address matches */ + len = arf_ack_decode_service_request(service_request, + service_len, &data); + if (len > 0) { + /* validate the parameters before storing data */ + if ((data.access == FILE_STREAM_ACCESS) && + (service_data->invoke_id == Current_Invoke_ID)) { + if (data.type.stream.fileStartPosition == 0) + pFile = fopen(Local_File_Name, "wb"); + else + pFile = fopen(Local_File_Name, "rb+"); + if (pFile) { + /* is there anything to do with this? data.stream.requestedOctetCount */ + (void) fseek(pFile, data.type.stream.fileStartPosition, + SEEK_SET); + octets_written = fwrite(octetstring_value(&data.fileData), 1, /* unit to write in bytes - in our case, an octet is one byte */ + octetstring_length(&data.fileData), pFile); + if (octets_written != octetstring_length(&data.fileData)) + fprintf(stderr, + "Unable to write data to file \"%s\".\n", + Local_File_Name); + else + printf("\r%u bytes", + (data.type.stream.fileStartPosition + + octets_written)); + fclose(pFile); + } + if (data.endOfFile) { + End_Of_File_Detected = true; + printf("\r\n"); + } + } + } +} + +static void LocalIAmHandler(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) src; + (void) service_len; + len = iam_decode_service_request(service_request, + &device_id, &max_apdu, &segmentation, &vendor_id); + if (len != -1) { + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +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, + LocalIAmHandler); + /* 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 the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + AtomicReadFileAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + Atomic_Read_File_Error_Handler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +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; + int fileStartPosition = 0; + unsigned requestedOctetCount = 0; + uint8_t invoke_id = 0; + bool found = false; + uint16_t my_max_apdu = 0; + + if (argc < 4) { + /* FIXME: what about access method - record or stream? */ + printf("%s device-instance file-instance local-name\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_File_Object_Instance = strtol(argv[2], NULL, 0); + Local_File_Name = argv[3]; + 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; + } + if (Target_File_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "file-instance=%u - it must be less than %u\r\n", + Target_File_Object_Instance, BACNET_MAX_INSTANCE + 1); + 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()); +#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 (End_Of_File_Detected || 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) { + /* calculate the smaller of our APDU size or theirs + and remove the overhead of the APDU (about 16 octets max). + note: we could fail if there is a bottle neck (router) + and smaller MPDU in betweeen. */ + if (max_apdu < MAX_APDU) + my_max_apdu = max_apdu; + else + my_max_apdu = MAX_APDU; + requestedOctetCount = my_max_apdu - 16; + /* has the previous invoke id expired or returned? + note: invoke ID = 0 is invalid, so it will be idle */ + if ((invoke_id == 0) || tsm_invoke_id_free(invoke_id)) { + if (invoke_id != 0) + fileStartPosition += requestedOctetCount; + /* we'll read the file in chunks + less than max_apdu to keep unsegmented */ + invoke_id = + Send_Atomic_Read_File_Stream + (Target_Device_Object_Instance, + Target_File_Object_Instance, fileStartPosition, + requestedOctetCount); + Current_Invoke_ID = invoke_id; + } 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) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/readfile/main.ide b/bacnet-stack-0-3-0/demo/readfile/main.ide new file mode 100644 index 00000000..ba4fbdc8 Binary files /dev/null and b/bacnet-stack-0-3-0/demo/readfile/main.ide differ diff --git a/bacnet-stack-0-3-0/demo/readfile/makefile.b32 b/bacnet-stack-0-3-0/demo/readfile/makefile.b32 new file mode 100644 index 00000000..0c5caad3 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readfile/makefile.b32 @@ -0,0 +1,155 @@ +# +# 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 = bacarf +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0;PRINT_ENABLED=1 + +SRCS = main.c \ + ..\..\ports\win32\bip-init.c \ + ..\..\bip.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\demo\handler\s_arfs.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\datetime.c \ + ..\..\filename.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 diff --git a/bacnet-stack-0-3-0/demo/readprop/Makefile b/bacnet-stack-0-3-0/demo/readprop/Makefile new file mode 100644 index 00000000..a1c54538 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readprop/Makefile @@ -0,0 +1,87 @@ +#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 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_ETHERNET=1 +#DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_ARCNET=1 +#DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_MSTP=1 +DEFINES = -DBACFILE=1 -DTSM_ENABLED=1 -DBIG_ENDIAN=0 -DPRINT_ENABLED=1 -DBACDL_BIP=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 = bacrp + +SRCS = readprop.c \ + $(BACNET_PORT)/bip-init.c \ + $(BACNET_PORT)/ethernet.c \ + $(BACNET_PORT)/arcnet.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_rp_a.c \ + $(BACNET_HANDLER)/s_rp.c \ + $(BACNET_HANDLER)/s_whois.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)/bacdcode.c \ + $(BACNET_ROOT)/bacapp.c \ + $(BACNET_ROOT)/bacprop.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/bacstr.c \ + $(BACNET_ROOT)/bactext.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)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/readprop/bacrp.cbp b/bacnet-stack-0-3-0/demo/readprop/bacrp.cbp new file mode 100644 index 00000000..570a5ad0 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readprop/bacrp.cbp @@ -0,0 +1,369 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/demo/readprop/makefile.b32 b/bacnet-stack-0-3-0/demo/readprop/makefile.b32 new file mode 100644 index 00000000..2ac4b190 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readprop/makefile.b32 @@ -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 = bacrp +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0;PRINT_ENABLED=1 + +SRCS = readprop.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_rp_a.c \ + ..\..\demo\handler\s_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\datetime.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\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 diff --git a/bacnet-stack-0-3-0/demo/readprop/readprop.c b/bacnet-stack-0-3-0/demo/readprop/readprop.c new file mode 100644 index 00000000..d0b887fa --- /dev/null +++ b/bacnet-stack-0-3-0/demo/readprop/readprop.c @@ -0,0 +1,249 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#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" +/* 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 uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT; +static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS; +static int32_t Target_Object_Index = BACNET_ARRAY_ALL; + +static BACNET_ADDRESS Target_Address; +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; +} + +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 the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, + MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +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; + + if (argc < 5) { + printf + ("%s device-instance object-type object-instance property [index]\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_Object_Type = strtol(argv[2], NULL, 0); + Target_Object_Instance = strtol(argv[3], NULL, 0); + Target_Object_Property = strtol(argv[4], NULL, 0); + if (argc > 5) + Target_Object_Index = strtol(argv[5], NULL, 0); + 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; + } + if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + if (Target_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); +#if defined(BACDL_ETHERNET) + /* init the physical layer */ + if (!ethernet_init("eth0")) + return 1; +#elif defined(BACDL_BIP) + bip_set_interface("eth0"); + if (!bip_init()) + return 1; + printf("bip: using port %hu\r\n", bip_get_port()); +#elif defined(BACDL_ARCNET) + if (!arcnet_init("arc0")) + return 1; +#else +#error Datalink (BACDL_ETHERNET,BACDL_BIP, or BACDL_ARCNET) undefined +#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_Read_Property_Request + (Target_Device_Object_Instance, Target_Object_Type, + Target_Object_Instance, Target_Object_Property, + Target_Object_Index); + } 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; +} diff --git a/bacnet-stack-0-3-0/demo/reinit/Makefile b/bacnet-stack-0-3-0/demo/reinit/Makefile new file mode 100644 index 00000000..afe96b16 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/reinit/Makefile @@ -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 = bacrd + +SRCS = main.c \ + $(BACNET_ROOT)/rd.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)/s_whois.c \ + $(BACNET_HANDLER)/s_rd.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)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/reinit/main.c b/bacnet-stack-0-3-0/demo/reinit/main.c new file mode 100644 index 00000000..e33118ff --- /dev/null +++ b/bacnet-stack-0-3-0/demo/reinit/main.c @@ -0,0 +1,268 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#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 "rd.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 BACNET_REINITIALIZED_STATE Reinitialize_State = + BACNET_REINIT_COLDSTART; +static char *Reinitialize_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 MyReinitializeDeviceSimpleAckHandler(BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + (void) src; + (void) invoke_id; + printf("ReinitializeDevice 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 the ack coming back */ + apdu_set_confirmed_simple_ack_handler + (SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + MyReinitializeDeviceSimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + 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 [password]\r\n" + "Send BACnet ReinitializeDevice service to device.\r\n" + "\r\n" + "The device-instance can be 0 to %d.\r\n" + "Possible state values:\r\n" + " 0=coldstart\r\n" + " 1=warmstart\r\n" + " 2=startbackup\r\n" + " 3=endbackup\r\n" + " 4=startrestore\r\n" + " 5=endrestore\r\n" + " 6=abortrestore\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); + Reinitialize_State = strtol(argv[2], NULL, 0); + /* optional password */ + if (argc > 3) + Reinitialize_Password = argv[3]; + + 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(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; +#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 + /* 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 = bip_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_Reinitialize_Device_Request + (Target_Device_Object_Instance, Reinitialize_State, + Reinitialize_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) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/reinit/makefile.b32 b/bacnet-stack-0-3-0/demo/reinit/makefile.b32 new file mode 100644 index 00000000..b59837f8 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/reinit/makefile.b32 @@ -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 = bacrd +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 \ + ..\..\rd.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\s_whois.c \ + ..\..\demo\handler\s_rd.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 diff --git a/bacnet-stack-0-3-0/demo/server/Makefile b/bacnet-stack-0-3-0/demo/server/Makefile new file mode 100644 index 00000000..0aec2d2a --- /dev/null +++ b/bacnet-stack-0-3-0/demo/server/Makefile @@ -0,0 +1,93 @@ +#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 -DPRINT_ENABLED=1 -DBIG_ENDIAN=0 -DBACDL_ETHERNET=1 +#DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBIG_ENDIAN=0 -DBACDL_ARCNET=1 +#DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBIG_ENDIAN=0 -DBACDL_MSTP=1 +DEFINES = -DBACFILE=1 -DPRINT_ENABLED=1 -DBIG_ENDIAN=0 -DBACDL_BIP=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 = bacserv + +SRCS = main.c \ + $(BACNET_PORT)/bip-init.c \ + $(BACNET_PORT)/ethernet.c \ + $(BACNET_PORT)/arcnet.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_wp.c \ + $(BACNET_HANDLER)/h_arf.c \ + $(BACNET_HANDLER)/h_rd.c \ + $(BACNET_HANDLER)/h_dcc.c \ + $(BACNET_HANDLER)/h_ts.c \ + $(BACNET_HANDLER)/h_whohas.c \ + $(BACNET_HANDLER)/s_ihave.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)/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)/whohas.c \ + $(BACNET_ROOT)/ihave.c \ + $(BACNET_ROOT)/rp.c \ + $(BACNET_ROOT)/wp.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/rd.c \ + $(BACNET_ROOT)/dcc.c \ + $(BACNET_ROOT)/timesync.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 + diff --git a/bacnet-stack-0-3-0/demo/server/epics_vts3.tpi b/bacnet-stack-0-3-0/demo/server/epics_vts3.tpi new file mode 100644 index 00000000..79a06669 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/server/epics_vts3.tpi @@ -0,0 +1,759 @@ +PICS 0 +BACnet Protocol Implementation Conformance Statement + +-- +-- +-- BACnet Stack Demo +-- bacnet.sourceforge.net +-- Author: Steve Karg +-- +-- + +Vendor Name: "ASHRAE" +Product Name: "SimpleServer" +Product Model Number: "GNU" +Product Description: "server" + +BIBBs Supported: +{ +-- The BIBBs may be any of: +-- DS-RP-A + DS-RP-B +-- DS-RPM-A DS-RPM-B +-- DS-RPC-A DS-RPC-B +-- DS-WP-A + DS-WP-B +-- DS-WPM-A DS-WPM-B +-- DS-COV-A DS-COV-B +-- DS-COVP-A DS-COVP-B +-- DS-COVU-A DS-COVU-B +-- AE-N-A AE-N-I-B AE-N-E-B +-- AE-ACK-A AE-ACK-B +-- AE-ASUM-A AE-ASUM-B +-- AE-ESUM-A AE-ESUM-B +-- AE-INFO-A AE-INFO-B +-- AE-LS-A AE-LS-B +-- SCHED-A SCHED-I-B SCHED-E-B +-- T-VMT-A T-VMT-I-B T-VMT-E-B +-- T-ATR-A T-ATR-B +-- DM-DDB-A + DM-DDB-B +-- DM-DOB-A + DM-DOB-B +-- DM-DCC-A + DM-DCC-B +-- DM-PT-A DM-PT-B +-- DM-TM-A DM-TM-B +-- DM-TS-A + DM-TS-B +-- DM-UTC-A + DM-UTC-B +-- DM-RD-A + DM-RD-B +-- DM-BR-A DM-BR-B +-- DM-R-A DM-R-B +-- DM-LM-A DM-LM-B +-- DM-OCD-A DM-OCD-B +-- DM-VT-A DM-VT-B +-- NM-CE-A NM-CE-B +-- NM-RC-A NM-RC-B +} + +BACnet Standard Application Services Supported: +{ +-- AcknowledgeAlarm Initiate Execute +-- ConfirmedCOVNotification Initiate Execute + UnconfirmedCOVNotification Initiate +-- ConfirmedEventNotification Initiate Execute +-- UnconfirmedEventNotification Initiate Execute +-- GetAlarmSummary Initiate Execute +-- GetEnrollmentSummary Initiate Execute + AtomicReadFile Initiate Execute +-- AtomicWriteFile Initiate Execute +-- AddListElement Initiate Execute +-- RemoveListElement Initiate Execute +-- CreateObject Initiate Execute +-- DeleteObject Initiate Execute + ReadProperty Initiate Execute +-- ReadpropertyConditional Initiate Execute +-- ReadPropertyMultiple Initiate Execute +-- SubscribeCOV Initiate Execute + WriteProperty Initiate Execute +-- WritePropertyMultiple Initiate Execute + DeviceCommunicationControl Initiate Execute +-- ConfirmedPrivateTransfer Initiate Execute +-- UnconfirmedPrivateTransfer Initiate Execute + TimeSynchronization Initiate Execute + Who-Has Initiate Execute + I-Have Initiate + Who-Is Initiate Execute + I-Am Initiate +-- VT-Open Initiate Execute +-- VT-Close Initiate Execute +-- VT-Data Initiate Execute +-- ConfirmedTextMessage Initiate Execute +-- UnconfirmedTextMessage Initiate Execute + ReinitializeDevice Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute + UTCTimeSynchronization Initiate Execute +-- ReadRange Initiate Execute +-- GetEventInformation Initiate Execute +-- LifeSafetyOperation Initiate Execute +-- SubscribeCOVProperty Initiate Execute +-- RequestKey Initiate Execute +-- Authenticate Initiate Execute +} + +Standard Object-Types Supported: +{ + Analog Input + Analog Output + Analog Value +-- Averaging Createable Deleteable + Binary Input + Binary Output + Binary Value +-- Calendar Createable Deleteable +-- Command Createable Deleteable + Device +-- Event Enrollment Createable Deleteable + File +-- Group Createable Deleteable +-- Loop Createable Deleteable +-- Multi-state Input Createable Deleteable +Multi-state Output +-- Multi-state Value Createable Deleteable +-- Notification Class Createable Deleteable +-- Program Createable Deleteable +-- Schedule Createable Deleteable + Life Safety Point +-- Life Safety Zone Createable Deleteable +-- Trend Log Createable Deleteable + Load Control +} + +Data Link Layer Option: +{ +-- ISO 8802-3, 10BASE5 +-- ISO 8802-3, 10BASE2 +-- ISO 8802-3, 10BASET +-- ISO 8802-3, Fiber +-- ARCNET, coax star +-- ARCNET, coax bus +-- ARCNET, twisted pair star +-- ARCNET, twisted pair bus +-- ARCNET, fiber star +-- MS/TP master. Baud rate(s): 9600 +-- MS/TP slave. Baud rate(s): 9600 +-- Point-To-Point. Modem, Baud rate(s): 14.4k +-- Point-To-Point. Modem, Autobaud range: 9600 to 28.8k + BACnet/IP, 'DIX' Ethernet +-- BACnet/IP, PPP +-- Other +} + +Character Sets Supported: +{ + ANSI X3.4 +-- Other Character Sets not supported +-- IBM/Microsoft DBCS +-- JIS C 6226 +-- ISO 10646 (ICS-4) +-- ISO 10646 (UCS2) +} + +Special Functionality: +{ + Maximum APDU size in octets: 480 -- Arcnet Maximum 501 less NL Header +-- Maximum APDU size in octets: 474 +-- Segmented Requests Supported, window size: 1 +-- Segmented Responses Supported, window size: 1 +-- Router +} + +List of Objects in test device: +{ + { + object-identifier: (device,123) + object-name: "SimpleServer" + object-type: device + system-status: operational + vendor-name: "ASHRAE" + vendor-identifier: 0 + model-name: "GNU" + firmware-revision: "1.0" + application-software-version: "1.0" + protocol-version: 1 + protocol-revision: 5 + protocol-conformance-class: 1 + protocol-services-supported: (F,F,F,F,F,F,T,F,F,F,F,F,T,F,F,T,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,F,T,T,T,F,T,F,F,F) + protocol-object-types-supported: (T,T,T,T,T,T,F,F,T,F,T,F,F,F,T,F,F,F,F,F,F,T,F,F,F) + max-apdu-length-accepted: 480 + segmentation-supported: no-segmentation + apdu-timeout: 3000 + number-of-apdu-retries: 3 + device-address-binding: ? + local-time: ? + local-date: ? + utc-offset: ? + daylight-savings-status: ? + database-revision: ? + object-list: { + (device,123),(analog-input,0),(analog-input,1), + (analog-input,2),(analog-input,3),(analog-input,4), + (analog-input,5),(analog-input,6),(analog-output,0), + (analog-output,1),(analog-output,2),(analog-output,3), + (analog-value,0),(analog-value,1),(analog-value,2), + (analog-value,3),(binary-input,0),(binary-input,1), + (binary-input,2),(binary-input,3),(binary-input,4), + (binary-output,0),(binary-output,1),(binary-output,2), + (binary-output,3),(binary-output,4),(binary-output,5), + (binary-value,0),(binary-value,1),(life-safety-point,0), + (life-safety-point,1),(life-safety-point,2),(life-safety-point,3), + (life-safety-point,4),(life-safety-point,5),(life-safety-point,6), + (multi-state-output,0),(multi-state-output,1),(multi-state-output,2), + (multi-state-output,3),(file,0),(file,1), + (file,2) + } + }, + { + object-identifier: (analog-input,0) + object-name: "ANALOG INPUT 0" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 0" + }, + { + object-identifier: (analog-input,1) + object-name: "ANALOG INPUT 1" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 1" + }, + { + object-identifier: (analog-input,2) + object-name: "ANALOG INPUT 2" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 2" + }, + { + object-identifier: (analog-input,3) + object-name: "ANALOG INPUT 3" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 3" + }, + { + object-identifier: (analog-input,4) + object-name: "ANALOG INPUT 4" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 4" + }, + { + object-identifier: (analog-input,5) + object-name: "ANALOG INPUT 5" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 5" + }, + { + object-identifier: (analog-input,6) + object-name: "ANALOG INPUT 6" + object-type: analog-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + description: "ANALOG INPUT 6" + }, + { + object-identifier: (analog-output,0) + object-name: "ANALOG OUTPUT 0" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 0" + }, + { + object-identifier: (analog-output,1) + object-name: "ANALOG OUTPUT 1" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 1" + }, + { + object-identifier: (analog-output,2) + object-name: "ANALOG OUTPUT 2" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 2" + }, + { + object-identifier: (analog-output,3) + object-name: "ANALOG OUTPUT 3" + object-type: analog-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + description: "ANALOG OUTPUT 3" + }, + { + object-identifier: (analog-value,0) + object-name: "ANALOG VALUE 0" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 0" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,1) + object-name: "ANALOG VALUE 1" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 1" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,2) + object-name: "ANALOG VALUE 2" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 2" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (analog-value,3) + object-name: "ANALOG VALUE 3" + object-type: analog-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + units: percent + relinquish-default: 0.000000 + description: "ANALOG VALUE 3" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: 0.000000 + }, + { + object-identifier: (binary-input,0) + object-name: "BINARY INPUT 0" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 0" + }, + { + object-identifier: (binary-input,1) + object-name: "BINARY INPUT 1" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 1" + }, + { + object-identifier: (binary-input,2) + object-name: "BINARY INPUT 2" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 2" + }, + { + object-identifier: (binary-input,3) + object-name: "BINARY INPUT 3" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 3" + }, + { + object-identifier: (binary-input,4) + object-name: "BINARY INPUT 4" + object-type: binary-input + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + description: "BINARY INPUT 4" + }, + { + object-identifier: (binary-output,0) + object-name: "BINARY OUTPUT 0" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 0" + }, + { + object-identifier: (binary-output,1) + object-name: "BINARY OUTPUT 1" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 1" + }, + { + object-identifier: (binary-output,2) + object-name: "BINARY OUTPUT 2" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 2" + }, + { + object-identifier: (binary-output,3) + object-name: "BINARY OUTPUT 3" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 3" + }, + { + object-identifier: (binary-output,4) + object-name: "BINARY OUTPUT 4" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 4" + }, + { + object-identifier: (binary-output,5) + object-name: "BINARY OUTPUT 5" + object-type: binary-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + polarity: normal + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + description: "BINARY OUTPUT 5" + }, + { + object-identifier: (binary-value,0) + object-name: "BINARY VALUE 0" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BINARY VALUE 0" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + }, + { + object-identifier: (binary-value,1) + object-name: "BINARY VALUE 1" + object-type: binary-value + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + description: "BINARY VALUE 1" + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + }, + { + object-identifier: (life-safety-point,0) + object-name: "LS POINT 0" + object-type: life-safety-point + description: "LS POINT 0" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,1) + object-name: "LS POINT 1" + object-type: life-safety-point + description: "LS POINT 1" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,2) + object-name: "LS POINT 2" + object-type: life-safety-point + description: "LS POINT 2" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,3) + object-name: "LS POINT 3" + object-type: life-safety-point + description: "LS POINT 3" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,4) + object-name: "LS POINT 4" + object-type: life-safety-point + description: "LS POINT 4" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,5) + object-name: "LS POINT 5" + object-type: life-safety-point + description: "LS POINT 5" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (life-safety-point,6) + object-name: "LS POINT 6" + object-type: life-safety-point + description: "LS POINT 6" + present-value: ? + device-type: ? + status-flags: (F,F,F,F) + event-state: normal + reliability: ? + out-of-service: F + mode: ? W + silenced: ? + operation-expected: ? + }, + { + object-identifier: (multi-state-output,0) + object-name: "MULTISTATE OUTPUT 0" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 0" + }, + { + object-identifier: (multi-state-output,1) + object-name: "MULTISTATE OUTPUT 1" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 1" + }, + { + object-identifier: (multi-state-output,2) + object-name: "MULTISTATE OUTPUT 2" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 2" + }, + { + object-identifier: (multi-state-output,3) + object-name: "MULTISTATE OUTPUT 3" + object-type: multi-state-output + present-value: ? + status-flags: (F,F,F,F) + event-state: normal + out-of-service: F + priority-array: {?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?} + relinquish-default: inactive + number-of-states: 254 + description: "MULTISTATE OUTPUT 3" + }, + { + object-identifier: (file,0) + object-name: "FILE 0" + object-type: file + file-type: "TEXT" + file-size: 0 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "test.log" + }, + { + object-identifier: (file,1) + object-name: "FILE 1" + object-type: file + file-type: "TEXT" + file-size: 0 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "script.txt" + }, + { + object-identifier: (file,2) + object-name: "FILE 2" + object-type: file + file-type: "TEXT" + file-size: 39582 + modification-date: ? + archive: ? W + read-only: T + file-access-method: stream-access + description: "bacenum.h" + } +} + +End of BACnet Protocol Implementation Conformance Statement + + diff --git a/bacnet-stack-0-3-0/demo/server/main.c b/bacnet-stack-0-3-0/demo/server/main.c new file mode 100644 index 00000000..8c12c527 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/server/main.c @@ -0,0 +1,168 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "dcc.h" +#include "net.h" +#include "txbuf.h" +#include "lc.h" + +/* This is an example server application using the BACnet Stack */ + +/* buffers used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void Init_Service_Handlers(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, + handler_who_has); + /* 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); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); + apdu_set_unconfirmed_handler + (SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +} + +static void cleanup(void) +{ + datalink_cleanup(); +} + +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"); + } +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + BACNET_ADDRESS my_address, broadcast_address; + time_t last_seconds = 0; + time_t current_seconds = 0; + + /* allow the device ID to be set */ + if (argc > 1) + Device_Set_Object_Instance_Number(strtol(argv[1], NULL, 0)); +#if defined(BACDL_BIP) + if (argc > 2) + bip_set_port(strtol(argv[2], NULL, 0)); +#endif + printf("BACnet Server Demo - Device #%u\r\n", + Device_Object_Instance_Number()); + 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()); +#endif +#ifdef BACDL_ARCNET + if (!arcnet_init("arc0")) + return 1; +#endif + datalink_get_broadcast_address(&broadcast_address); + print_address("Broadcast", &broadcast_address); + datalink_get_my_address(&my_address); + print_address("Address", &my_address); + atexit(cleanup); + /* configure the timeout values */ + last_seconds = time(NULL); + /* broadcast an I-Am on startup */ + iam_send(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + /* input */ + 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) { + dcc_timer_seconds(current_seconds - last_seconds); + Load_Control_State_Machine_Handler(); + } + /* output */ + + /* blink LEDs, Turn on or off outputs, etc */ + } +} diff --git a/bacnet-stack-0-3-0/demo/server/makefile.b32 b/bacnet-stack-0-3-0/demo/server/makefile.b32 new file mode 100644 index 00000000..a57195c6 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/server/makefile.b32 @@ -0,0 +1,164 @@ +# +# 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 = bacserv +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +# Note: unless some other drivers are installed, BIP is the only +# datalink layer that Win32 supports +DEFINES = -DBACDL_BIP=1;USE_INADDR=1;PRINT_ENABLED=1;BIG_ENDIAN=0 + +SRCS = main.c \ + ..\..\ports\win32\bip-init.c \ + ..\..\bip.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\h_wp.c \ + ..\..\demo\handler\h_arf.c \ + ..\..\demo\handler\h_rd.c \ + ..\..\demo\handler\h_dcc.c \ + ..\..\demo\handler\h_ts.c \ + ..\..\demo\handler\h_whohas.c \ + ..\..\demo\handler\s_ihave.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\datetime.c \ + ..\..\indtext.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\whohas.c \ + ..\..\ihave.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.c \ + ..\..\rd.c \ + ..\..\dcc.c \ + ..\..\timesync.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 \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_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 : $(BCC_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 $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +$(BCC_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 diff --git a/bacnet-stack-0-3-0/demo/timesync/Makefile b/bacnet-stack-0-3-0/demo/timesync/Makefile new file mode 100644 index 00000000..d4976676 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/timesync/Makefile @@ -0,0 +1,87 @@ +#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=0 -DBACDL_BIP=1 -DUSE_INADDR=0 -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 = bacts + +SRCS = main.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_ihave.c \ + $(BACNET_HANDLER)/h_iam.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/h_ts.c \ + $(BACNET_HANDLER)/s_ts.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)/lsp.c \ + $(BACNET_OBJECT)/lc.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)/indtext.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/whohas.c \ + $(BACNET_ROOT)/ihave.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.c \ + $(BACNET_ROOT)/timesync.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 + diff --git a/bacnet-stack-0-3-0/demo/timesync/main.c b/bacnet-stack-0-3-0/demo/timesync/main.c new file mode 100644 index 00000000..898eed4a --- /dev/null +++ b/bacnet-stack-0-3-0/demo/timesync/main.c @@ -0,0 +1,214 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "net.h" +#include "datalink.h" +#include "timesync.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 */ +#if 0 +static int32_t Target_Object_Instance_Min = 0; +static int32_t Target_Object_Instance_Max = BACNET_MAX_INSTANCE; +#endif + +static bool Error_Detected = false; + +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; +} + +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); + /* 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 the reply (request) coming in */ + apdu_set_unconfirmed_handler + (SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); + /* handle any errors coming back */ + 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 */ + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; + time_t rawtime; + struct tm *my_time; + BACNET_DATE bdate; + BACNET_TIME btime; + +#ifdef BIP_DEBUG + BACNET_ADDRESS my_address, broadcast_address; +#endif + + /* FIXME: we could send directed time sync, and how do we send UTC? */ +#if 0 + if (argc < 2) { + printf("Usage: %s date time [device-instance]\r\n" + "Send BACnet TimeSynchronization request to all devices.\r\n" + "date format: year/month/day:dayofweek (e.g. 2006/4/1:6)\r\n" + "year: AD, such as 2006\r\n" + "month: 1=January, 12=December\r\n" + "day: 1-31\r\n" + "dayofweek: 1=Monday, 7=Sunday\r\n" + "\r\n" + "time format: hour:minute:second.hundredths (e.g. 23:59:59.12)\r\n" + "hour: 0-23\r\n" + "minute: 0-59\r\n" + "second: 0-59\r\n" + "hundredths: 0-99\r\n" + "\r\n" + "Optional device-instance sends a unicast time sync.\r\n", + filename_remove_path(argv[0])); + return 0; + } +#else + (void) argc; + (void) argv; +#endif + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; +#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 + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = Device_APDU_Timeout() / 1000; + /* send the request */ + time(&rawtime); + my_time = localtime(&rawtime); + bdate.year = my_time->tm_year + 1900; + bdate.month = my_time->tm_mon + 1; + bdate.day = my_time->tm_mday; + bdate.wday = my_time->tm_wday ? my_time->tm_wday : 7; + btime.hour = my_time->tm_hour; + btime.min = my_time->tm_min; + btime.sec = my_time->tm_sec; + btime.hundredths = 0; + Send_TimeSync(&bdate, &btime); + /* loop forever - not necessary for time sync, but we can watch */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/timesync/makefile.b32 b/bacnet-stack-0-3-0/demo/timesync/makefile.b32 new file mode 100644 index 00000000..0cd45a10 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/timesync/makefile.b32 @@ -0,0 +1,163 @@ +# +# 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 = bacts +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=0;USE_INADDR=0;BIG_ENDIAN=0;PRINT_ENABLED=1 +# Directories +BACNET_PORT = ..\..\ports\win32 +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +BACNET_ROOT = ..\.. + +SRCS = main.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_iam.c \ + $(BACNET_HANDLER)\h_rp.c \ + $(BACNET_HANDLER)\h_ts.c \ + $(BACNET_HANDLER)\s_ts.c \ + $(BACNET_OBJECT)\bacfile.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_ROOT)\address.c \ + $(BACNET_ROOT)\filename.c \ + $(BACNET_ROOT)\bacdcode.c \ + $(BACNET_ROOT)\bacapp.c \ + $(BACNET_ROOT)\bacstr.c \ + $(BACNET_ROOT)\bactext.c \ + $(BACNET_ROOT)\indtext.c \ + $(BACNET_ROOT)\datetime.c \ + $(BACNET_ROOT)\whois.c \ + $(BACNET_ROOT)\iam.c \ + $(BACNET_ROOT)\whohas.c \ + $(BACNET_ROOT)\ihave.c \ + $(BACNET_ROOT)\rd.c \ + $(BACNET_ROOT)\rp.c \ + $(BACNET_ROOT)\wp.c \ + $(BACNET_ROOT)\arf.c \ + $(BACNET_ROOT)\awf.c \ + $(BACNET_ROOT)\dcc.c \ + $(BACNET_ROOT)\timesync.c \ + $(BACNET_ROOT)\abort.c \ + $(BACNET_ROOT)\reject.c \ + $(BACNET_ROOT)\bacerror.c \ + $(BACNET_ROOT)\apdu.c \ + $(BACNET_ROOT)\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;$(BACNET_ROOT)\;$(BACNET_OBJECT)\;$(BACNET_HANDLER)\;$(BACNET_PORT)\;. + +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 $(BACNET_ROOT)\*.obj + del $(BACNET_HANDLER)\*.obj + del $(BACNET_OBJECT)\*.obj + del $(BACNET_PORT)\*.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 diff --git a/bacnet-stack-0-3-0/demo/ucov/Makefile b/bacnet-stack-0-3-0/demo/ucov/Makefile new file mode 100644 index 00000000..11dfcbb1 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/ucov/Makefile @@ -0,0 +1,82 @@ +#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=0 -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 = bacucov + +SRCS = main.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_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)/indtext.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/cov.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/ucov/main.c b/bacnet-stack-0-3-0/demo/ucov/main.c new file mode 100644 index 00000000..775d2f72 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/ucov/main.c @@ -0,0 +1,221 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "cov.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" +/* some demo stuff needed */ +#include "filename.h" +#include "handlers.h" +#include "client.h" +#include "txbuf.h" + +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); +} + +int main(int argc, char *argv[]) +{ + char *value_string = NULL; + bool status = false; + BACNET_COV_DATA cov_data; + uint8_t tag; + + if (argc < 7) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s pid device-id object-type object-instance " + "time property tag value [priority] [index]\r\n" + "\r\n" + "pid:\r\n" + "Process Identifier for this broadcast.\r\n" + "\r\n" + "device-id:\r\n" + "The Initiating BACnet Device Object Instance number.\r\n" + "\r\n" + "object-type:\r\n" + "The monitored object type is the integer value of the\r\n" + "enumeration BACNET_OBJECT_TYPE in bacenum.h. For example,\r\n" + "if you were monitoring Analog Output 2, the object-type\r\n" + "would be 1.\r\n" + "\r\n" + "object-instance:\r\n" + "The monitored object instance number.\r\n" + "\r\n" + "time:\r\n" + "The subscription time remaining is conveyed in seconds.\r\n" + "\r\n" + "property:\r\n" + "The property is an integer value of the enumeration \r\n" + "BACNET_PROPERTY_ID in bacenum.h. For example, if you were\r\n" + "monitoring the Present Value property, you would use 85\r\n" + "as the property.\r\n" + "\r\n" + "tag:\r\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n" + "in bacenum.h. It is the data type of the value that you are\r\n" + "monitoring. For example, if you were monitoring a REAL value, you would \r\n" + "use a tag of 4." + "\r\n" + "value:\r\n" + "The value is an ASCII representation of some type of data that you\r\n" + "are monitoring. It is encoded using the tag information provided. For\r\n" + "example, if you were writing a REAL value of 100.0, you would use \r\n" + "100.0 as the value.\r\n" + "\r\n" + "[priority]:\r\n" + "This optional parameter is used for reporting the priority of the\r\n" + "value. If no priority is given, none is sent, and the BACnet \r\n" + "standard requires that the value is reported at the lowest \r\n" + "priority (16) if the object property supports priorities.\r\n" + "\r\n" + "[index]\r\n" + "This optional integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can be reported.\r\n" + "\r\n" + "Here is a brief overview of BACnet property and tags:\r\n" + "Certain properties are expected to be written with certain \r\n" + "application tags, so you probably need to know which ones to use\r\n" + "with each property of each object. It is almost safe to say that\r\n" + "given a property and an object and a table, the tag could be looked\r\n" + "up automatically. There may be a few exceptions to this, such as\r\n" + "the Any property type in the schedule object and the Present Value\r\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n" + "the demo to use this kind of table - but I also wanted to be able\r\n" + "to do negative testing by passing the wrong tag and have the server\r\n" + "return a reject message.\r\n" + "\r\n" + "Example:\r\n" + "If you want generate an unconfirmed COV,\r\n" + "you could send the following command:\r\n" + "%s 1 2 3 4 5 85 4 100.0\r\n" + "where 1=pid, 2=device-id, 3=AV, 4=object-id, 5=time,\r\n" + "85=Present-Value, 4=REAL, 100.0=value\r\n", + filename_remove_path(argv[0]), filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + cov_data.subscriberProcessIdentifier = strtol(argv[1], NULL, 0); + cov_data.initiatingDeviceIdentifier = strtol(argv[2], NULL, 0); + cov_data.monitoredObjectIdentifier.type = strtol(argv[3], NULL, 0); + cov_data.monitoredObjectIdentifier.instance = strtol(argv[4], NULL, 0); + cov_data.timeRemaining = strtol(argv[5], NULL, 0); + cov_data.listOfValues.propertyIdentifier = strtol(argv[6], NULL, 0); + tag = strtol(argv[7], NULL, 0); + value_string = argv[8]; + /* optional priority */ + if (argc > 9) + cov_data.listOfValues.priority = strtol(argv[9], NULL, 0); + else + cov_data.listOfValues.priority = BACNET_NO_PRIORITY; + /* optional index */ + if (argc > 10) + cov_data.listOfValues.propertyArrayIndex = + strtol(argv[10], NULL, 0); + else + cov_data.listOfValues.propertyArrayIndex = BACNET_ARRAY_ALL; + + if (cov_data.initiatingDeviceIdentifier >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "device-instance=%u - it must be less than %u\r\n", + cov_data.initiatingDeviceIdentifier, BACNET_MAX_INSTANCE); + return 1; + } + if (cov_data.monitoredObjectIdentifier.type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.type, + MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + if (cov_data.monitoredObjectIdentifier.instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + cov_data.monitoredObjectIdentifier.instance, + BACNET_MAX_INSTANCE + 1); + return 1; + } + if (cov_data.listOfValues.propertyIdentifier > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + cov_data.listOfValues.propertyIdentifier, + MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + if (tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "tag=%u - it must be less than %u\r\n", + tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = bacapp_parse_application_data(tag, + value_string, &cov_data.listOfValues.value); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "unable to parse the tag value\r\n"); + return 1; + } + + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; + + /* only one value in our value list */ + cov_data.listOfValues.next = NULL; + + ucov_notify_send(&Handler_Transmit_Buffer[0], &cov_data); + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/ucov/makefile.b32 b/bacnet-stack-0-3-0/demo/ucov/makefile.b32 new file mode 100644 index 00000000..3ca3a77d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/ucov/makefile.b32 @@ -0,0 +1,153 @@ +# +# 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 = bacucov +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=0;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 \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\datetime.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.c \ + ..\..\cov.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 \ + ..\..\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 diff --git a/bacnet-stack-0-3-0/demo/whohas/Makefile b/bacnet-stack-0-3-0/demo/whohas/Makefile new file mode 100644 index 00000000..9fb81dc7 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whohas/Makefile @@ -0,0 +1,87 @@ +#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=0 -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 = bacwh + +SRCS = main.c \ + $(BACNET_ROOT)/rd.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_ihave.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/s_whohas.c \ + $(BACNET_HANDLER)/s_ihave.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)/whohas.c \ + $(BACNET_ROOT)/ihave.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/whohas/main.c b/bacnet-stack-0-3-0/demo/whohas/main.c new file mode 100644 index 00000000..22aeea4d --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whohas/main.c @@ -0,0 +1,210 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#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 "whohas.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 BACNET_OBJECT_TYPE Target_Object_Type = MAX_BACNET_OBJECT_TYPE; +static uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static char *Target_Object_Name = NULL; + +static bool Error_Detected = false; + +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; +} + +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); + /* 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 the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_HAVE, + handler_i_have); + /* handle any errors coming back */ + 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 */ + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; +#ifdef BIP_DEBUG + BACNET_ADDRESS my_address, broadcast_address; +#endif + + if (argc < 2) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s \r\n" + "Send BACnet WhoHas request to devices, and wait for responses.\r\n" + "\r\n" + "Use either:\r\n" + "The object-type can be 0 to %d.\r\n" + "The object-instance can be 0 to %d.\r\n" + "or:\r\n" + "The object-name can be any string of characters.\r\n", + filename_remove_path(argv[0]), + MAX_BACNET_OBJECT_TYPE - 1, BACNET_MAX_INSTANCE); + return 0; + } + /* decode the command line parameters */ + if (argc < 3) { + Target_Object_Name = argv[1]; + } else { + Target_Object_Type = strtol(argv[1], NULL, 0); + Target_Object_Instance = strtol(argv[2], NULL, 0); + if (Target_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; +#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 + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = Device_APDU_Timeout() / 1000; + /* send the request */ + if (argc < 3) + Send_WhoHas_Name(-1, -1, Target_Object_Name); + else + Send_WhoHas_Object(-1, -1, + Target_Object_Type, Target_Object_Instance); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/whohas/makefile.b32 b/bacnet-stack-0-3-0/demo/whohas/makefile.b32 new file mode 100644 index 00000000..22b263ed --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whohas/makefile.b32 @@ -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 = bacwh +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=0;USE_INADDR=1;BIG_ENDIAN=0;PRINT_ENABLED=1 + +SRCS = main.c \ + ..\..\rd.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_ihave.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\s_ihave.c \ + ..\..\demo\handler\s_whohas.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\datetime.c \ + ..\..\indtext.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\whohas.c \ + ..\..\ihave.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 \ + ..\..\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 diff --git a/bacnet-stack-0-3-0/demo/whois/Makefile b/bacnet-stack-0-3-0/demo/whois/Makefile new file mode 100644 index 00000000..cd4e4bbf --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whois/Makefile @@ -0,0 +1,87 @@ +#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=0 -DBACDL_BIP=1 -DUSE_INADDR=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 = bacwi + +SRCS = main.c \ + $(BACNET_ROOT)/rd.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_ihave.c \ + $(BACNET_HANDLER)/h_iam.c \ + $(BACNET_HANDLER)/h_rp.c \ + $(BACNET_HANDLER)/s_ihave.c \ + $(BACNET_HANDLER)/s_whois.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)/indtext.c \ + $(BACNET_ROOT)/datetime.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/whohas.c \ + $(BACNET_ROOT)/ihave.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/whois/main.c b/bacnet-stack-0-3-0/demo/whois/main.c new file mode 100644 index 00000000..1231645a --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whois/main.c @@ -0,0 +1,227 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.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" +/* 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 int32_t Target_Object_Instance_Min = 0; +static int32_t Target_Object_Instance_Max = BACNET_MAX_INSTANCE; + +static bool Error_Detected = false; + +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; +} + +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); + /* 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 the reply (request) coming back */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, + handler_i_am_add); + /* handle any errors coming back */ + 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 + +static void print_address_cache(void) +{ + int i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + time_t elapsed_seconds = 0; + time_t last_seconds = 0; + time_t current_seconds = 0; + time_t timeout_seconds = 0; +#ifdef BIP_DEBUG + BACNET_ADDRESS my_address, broadcast_address; +#endif + + if (argc < 2) { + printf + ("Usage: %s device-instance | device-instance-min device-instance-max\r\n" + "Send BACnet WhoIs request to devices, and wait for responses.\r\n" + "\r\n" "The device-instance can be 0 to %d, or -1 for ALL.\r\n" + "The device-instance can also be specified as a range.\r\n", + filename_remove_path(argv[0]), BACNET_MAX_INSTANCE); + return 0; + } + /* decode the command line parameters */ + if (argc < 3) { + Target_Object_Instance_Min = strtol(argv[1], NULL, 0); + Target_Object_Instance_Max = Target_Object_Instance_Min; + if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance-min=%u - it must be less than %u\r\n", + Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); + return 1; + } + } else { + Target_Object_Instance_Min = strtol(argv[1], NULL, 0); + Target_Object_Instance_Max = strtol(argv[2], NULL, 0); + if (Target_Object_Instance_Min > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance-min=%u - it must be less than %u\r\n", + Target_Object_Instance_Min, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Instance_Max > BACNET_MAX_INSTANCE) { + fprintf(stderr, + "object-instance-max=%u - it must be less than %u\r\n", + Target_Object_Instance_Max, BACNET_MAX_INSTANCE + 1); + return 1; + } + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; +#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 + /* configure the timeout values */ + last_seconds = time(NULL); + timeout_seconds = Device_APDU_Timeout() / 1000; + /* send the request */ + Send_WhoIs(Target_Object_Instance_Min, Target_Object_Instance_Max); + /* loop forever */ + for (;;) { + /* increment timer - exit if timed out */ + current_seconds = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (Error_Detected) + break; + /* increment timer - exit if timed out */ + elapsed_seconds += (current_seconds - last_seconds); + if (elapsed_seconds > timeout_seconds) + break; + /* keep track of time for next check */ + last_seconds = current_seconds; + } + print_address_cache(); + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/whois/makefile.b32 b/bacnet-stack-0-3-0/demo/whois/makefile.b32 new file mode 100644 index 00000000..2ecce488 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/whois/makefile.b32 @@ -0,0 +1,161 @@ +# +# 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 = bacwi +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=0;USE_INADDR=1;BIG_ENDIAN=0;PRINT_ENABLED=1 +# Directories +BACNET_PORT = ..\..\ports\win32 +BACNET_OBJECT = ..\object +BACNET_HANDLER = ..\handler +BACNET_ROOT = ..\.. + +SRCS = main.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_iam.c \ + $(BACNET_HANDLER)\h_rp.c \ + $(BACNET_HANDLER)\s_whois.c \ + $(BACNET_OBJECT)\bacfile.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_ROOT)\address.c \ + $(BACNET_ROOT)\filename.c \ + $(BACNET_ROOT)\bacdcode.c \ + $(BACNET_ROOT)\bacapp.c \ + $(BACNET_ROOT)\bacstr.c \ + $(BACNET_ROOT)\bactext.c \ + $(BACNET_ROOT)\indtext.c \ + $(BACNET_ROOT)\datetime.c \ + $(BACNET_ROOT)\whois.c \ + $(BACNET_ROOT)\iam.c \ + $(BACNET_ROOT)\whohas.c \ + $(BACNET_ROOT)\ihave.c \ + $(BACNET_ROOT)\rd.c \ + $(BACNET_ROOT)\rp.c \ + $(BACNET_ROOT)\wp.c \ + $(BACNET_ROOT)\arf.c \ + $(BACNET_ROOT)\awf.c \ + $(BACNET_ROOT)\dcc.c \ + $(BACNET_ROOT)\abort.c \ + $(BACNET_ROOT)\reject.c \ + $(BACNET_ROOT)\bacerror.c \ + $(BACNET_ROOT)\apdu.c \ + $(BACNET_ROOT)\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;$(BACNET_ROOT)\;$(BACNET_OBJECT)\;$(BACNET_HANDLER)\;$(BACNET_PORT)\;. + +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 $(BACNET_ROOT)\*.obj + del $(BACNET_HANDLER)\*.obj + del $(BACNET_OBJECT)\*.obj + del $(BACNET_PORT)\*.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 diff --git a/bacnet-stack-0-3-0/demo/writefile/Makefile b/bacnet-stack-0-3-0/demo/writefile/Makefile new file mode 100644 index 00000000..c3fc7798 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writefile/Makefile @@ -0,0 +1,84 @@ +#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 = bacawf + +SRCS = writefile.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)/s_whois.c \ + $(BACNET_HANDLER)/s_awfs.c \ + $(BACNET_ROOT)/rp.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)/filename.c \ + $(BACNET_ROOT)/bigend.c \ + $(BACNET_ROOT)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/tsm.c \ + $(BACNET_ROOT)/address.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)/arf.c \ + $(BACNET_ROOT)/awf.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/writefile/makefile.b32 b/bacnet-stack-0-3-0/demo/writefile/makefile.b32 new file mode 100644 index 00000000..10a80170 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writefile/makefile.b32 @@ -0,0 +1,156 @@ +# +# 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 = bacawf +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0;PRINT_ENABLED=1 + +SRCS = writefile.c \ + ..\..\ports\win32\bip-init.c \ + ..\..\bip.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\demo\handler\s_awfs.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\datetime.c \ + ..\..\indtext.c \ + ..\..\filename.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 +# +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_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 : $(BCC_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 $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +$(BCC_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 diff --git a/bacnet-stack-0-3-0/demo/writefile/writefile.c b/bacnet-stack-0-3-0/demo/writefile/writefile.c new file mode 100644 index 00000000..36111536 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writefile/writefile.c @@ -0,0 +1,286 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the reply */ +#include +#include +#include +#include +#include /* for time */ +#include +#include "bactext.h" +#include "iam.h" +#include "awf.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" +/* 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_File_Object_Instance = 4194303; +static uint32_t Target_Device_Object_Instance = 4194303; +static BACNET_ADDRESS Target_Address; +static char *Local_File_Name = NULL; +static bool End_Of_File_Detected = false; +static bool Error_Detected = false; +static uint8_t Current_Invoke_ID = 0; + +static void Atomic_Read_File_Error_Handler(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("\r\nBACnet Error!\r\n"); + printf("Error Class: %s\r\n", bactext_error_class_name(error_class)); + printf("Error Code: %s\r\n", 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("\r\nBACnet Abort!\r\n"); + printf("Abort Reason: %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("\r\nBACnet Reject!\r\n"); + printf("Reject Reason: %s\r\n", + bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +static void LocalIAmHandler(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) src; + (void) service_len; + len = iam_decode_service_request(service_request, + &device_id, &max_apdu, &segmentation, &vendor_id); + if (len != -1) { + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +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, + LocalIAmHandler); + /* 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 any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + Atomic_Read_File_Error_Handler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +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; + int fileStartPosition = 0; + unsigned requestedOctetCount = 0; + uint8_t invoke_id = 0; + bool found = false; + uint16_t my_max_apdu = 0; + FILE *pFile = NULL; + static BACNET_OCTET_STRING fileData; + size_t len = 0; + + if (argc < 4) { + /* FIXME: what about access method - record or stream? */ + printf("%s device-instance file-instance local-name\r\n", + filename_remove_path(argv[0])); + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_File_Object_Instance = strtol(argv[2], NULL, 0); + Local_File_Name = argv[3]; + 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; + } + if (Target_File_Object_Instance >= BACNET_MAX_INSTANCE) { + fprintf(stderr, "file-instance=%u - it must be less than %u\r\n", + Target_File_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; + /* 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 = bip_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 (End_Of_File_Detected || Error_Detected) { + printf("\r\n"); + 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) { + /* calculate the smaller of our APDU size or theirs + and remove the overhead of the APDU (varies depending on size). + note: we could fail if there is a bottle neck (router) + and smaller MPDU in betweeen. */ + if (max_apdu < MAX_APDU) + my_max_apdu = max_apdu; + else + my_max_apdu = MAX_APDU; + /* Typical sizes are 50, 128, 206, 480, 1024, and 1476 octets */ + if (my_max_apdu <= 50) + requestedOctetCount = my_max_apdu - 16; + else if (my_max_apdu <= 480) + requestedOctetCount = my_max_apdu - 32; + else if (my_max_apdu <= 1476) + requestedOctetCount = my_max_apdu - 64; + else + requestedOctetCount = my_max_apdu / 2; + /* has the previous invoke id expired or returned? + note: invoke ID = 0 is invalid, so it will be idle */ + if ((invoke_id == 0) || tsm_invoke_id_free(invoke_id)) { + if (invoke_id != 0) + fileStartPosition += requestedOctetCount; + /* we'll read the file in chunks + less than max_apdu to keep unsegmented */ + pFile = fopen(Local_File_Name, "rb"); + if (pFile) { + (void) fseek(pFile, fileStartPosition, SEEK_SET); + len = fread(octetstring_value(&fileData), 1, + requestedOctetCount, pFile); + if (len < requestedOctetCount) + End_Of_File_Detected = true; + octetstring_truncate(&fileData, len); + fclose(pFile); + } else + End_Of_File_Detected = true; + printf("\rSending %d bytes", (fileStartPosition + len)); + invoke_id = + Send_Atomic_Write_File_Stream + (Target_Device_Object_Instance, + Target_File_Object_Instance, fileStartPosition, + &fileData); + Current_Invoke_ID = invoke_id; + } 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) { + fprintf(stderr, "\rError: APDU Timeout!\r\n"); + break; + } + } + /* keep track of time for next check */ + last_seconds = current_seconds; + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/demo/writefile/writefile.ide b/bacnet-stack-0-3-0/demo/writefile/writefile.ide new file mode 100644 index 00000000..df787ac9 Binary files /dev/null and b/bacnet-stack-0-3-0/demo/writefile/writefile.ide differ diff --git a/bacnet-stack-0-3-0/demo/writeprop/Makefile b/bacnet-stack-0-3-0/demo/writeprop/Makefile new file mode 100644 index 00000000..d69edd8f --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writeprop/Makefile @@ -0,0 +1,84 @@ +#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 = bacwp + +SRCS = main.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)/s_wp.c \ + $(BACNET_HANDLER)/s_whois.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)/whois.c \ + $(BACNET_ROOT)/iam.c \ + $(BACNET_ROOT)/tsm.c \ + $(BACNET_ROOT)/address.c \ + $(BACNET_ROOT)/arf.c \ + $(BACNET_ROOT)/dcc.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 + diff --git a/bacnet-stack-0-3-0/demo/writeprop/main.c b/bacnet-stack-0-3-0/demo/writeprop/main.c new file mode 100644 index 00000000..29a43578 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writeprop/main.c @@ -0,0 +1,376 @@ +/************************************************************************** +* +* Copyright (C) 2006-2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 that sends a BACnet service, and displays the response */ +#include +#include +#include +#include +#include /* for time */ +#include +#include +#include /* toupper */ +#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" +/* 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 uint32_t Target_Object_Instance = BACNET_MAX_INSTANCE; +static BACNET_OBJECT_TYPE Target_Object_Type = OBJECT_ANALOG_INPUT; +static BACNET_PROPERTY_ID Target_Object_Property = PROP_ACKED_TRANSITIONS; +/* array index value or BACNET_ARRAY_ALL */ +static int32_t Target_Object_Property_Index = BACNET_ARRAY_ALL; +#define MAX_PROPERTY_VALUES 16 +static BACNET_APPLICATION_DATA_VALUE + Target_Object_Property_Value[MAX_PROPERTY_VALUES]; + +/* 0 if not set, 1..16 if set */ +static uint8_t Target_Object_Property_Priority = 0; + +static BACNET_ADDRESS Target_Address; +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("\r\nBACnet Error!\r\n"); + printf("Error Class: %s\r\n", bactext_error_class_name(error_class)); + printf("Error Code: %s\r\n", 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("\r\nBACnet Abort!\r\n"); + printf("Abort Reason: %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("\r\nBACnet Reject!\r\n"); + printf("Reject Reason: %s\r\n", + bactext_reject_reason_name(reject_reason)); + Error_Detected = true; +} + +void MyWritePropertySimpleAckHandler(BACNET_ADDRESS * src, + uint8_t invoke_id) +{ + (void) src; + (void) invoke_id; + printf("\r\nWriteProperty 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 the ack coming back */ + apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + MyWritePropertySimpleAckHandler); + /* handle any errors coming back */ + apdu_set_error_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + MyErrorHandler); + apdu_set_abort_handler(MyAbortHandler); + apdu_set_reject_handler(MyRejectHandler); +} + +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; + char *value_string = NULL; + bool status = false; + int args_remaining = 0, tag_value_arg = 0, i = 0; + BACNET_APPLICATION_TAG property_tag; + uint8_t context_tag = 0; + + if (argc < 9) { + /* note: priority 16 and 0 should produce the same end results... */ + printf("Usage: %s device-instance object-type object-instance " + "property priority index tag value [tag value...]\r\n", + filename_remove_path(argv[0])); + if ((argc > 1) && (strcmp(argv[1], "--help") == 0)) { + printf("device-instance:\r\n" + "BACnet Device Object Instance number that you are trying to\r\n" + "communicate to. This number will be used to try and bind with\r\n" + "the device using Who-Is and I-Am services. For example, if you were\r\n" + "writing to Device Object 123, the device-instance would be 123.\r\n" + "\r\n" + "object-type:\r\n" + "The object type is the integer value of the enumeration\r\n" + "BACNET_OBJECT_TYPE in bacenum.h. It is the object that you are\r\n" + "writing to. For example if you were writing to Analog Output 2, \r\n" + "the object-type would be 1.\r\n" + "\r\n" + "object-instance:\r\n" + "This is the object instance number of the object that you are \r\n" + "writing to. For example, if you were writing to Analog Output 2, \r\n" + "the object-instance would be 2.\r\n" + "\r\n" + "property:\r\n" + "The property is an integer value of the enumeration \r\n" + "BACNET_PROPERTY_ID in bacenum.h. It is the property you are \r\n" + "writing to. For example, if you were writing to the Present Value\r\n" + "property, you would use 85 as the property.\r\n" + "\r\n" + "priority:\r\n" + "This parameter is used for setting the priority of the\r\n" + "write. If Priority 0 is given, no priority is sent. The BACnet \r\n" + "standard states that the value is written at the lowest \r\n" + "priority (16) if the object property supports priorities\r\n" + "when no priority is sent.\r\n" + "\r\n" + "index\r\n" + "This integer parameter is the index number of an array.\r\n" + "If the property is an array, individual elements can be written\r\n" + "to if supported. If this parameter is -1, the index is ignored.\r\n" + "\r\n" + "tag:\r\n" + "Tag is the integer value of the enumeration BACNET_APPLICATION_TAG \r\n" + "in bacenum.h. It is the data type of the value that you are\r\n" + "writing. For example, if you were writing a REAL value, you would \r\n" + "use a tag of 4.\r\n" + "Context tags are created using two tags in a row. The context tag\r\n" + "is preceded by a C. Ctag tag. C2 4 creates a context 2 tagged REAL.\r\n" + "\r\n" + "value:\r\n" + "The value is an ASCII representation of some type of data that you\r\n" + "are writing. It is encoded using the tag information provided. For\r\n" + "example, if you were writing a REAL value of 100.0, you would use \r\n" + "100.0 as the value.\r\n" + "\r\n" + "Here is a brief overview of BACnet property and tags:\r\n" + "Certain properties are expected to be written with certain \r\n" + "application tags, so you probably need to know which ones to use\r\n" + "with each property of each object. It is almost safe to say that\r\n" + "given a property and an object and a table, the tag could be looked\r\n" + "up automatically. There may be a few exceptions to this, such as\r\n" + "the Any property type in the schedule object and the Present Value\r\n" + "accepting REAL, BOOLEAN, NULL, etc. Perhaps it would be simpler for\r\n" + "the demo to use this kind of table - but I also wanted to be able\r\n" + "to do negative testing by passing the wrong tag and have the server\r\n" + "return a reject message.\r\n" + "\r\n" + "Example:\r\n" + "If you want send a 100 to the Present-Value in the Analog Output\r\n" + "at priority 16, you could send the following command:\r\n" + "%s 123 1 0 85 4 100\r\n" + "You could also send a relinquish command:\r\n" + "%s 123 1 0 85 0 0\r\n", + filename_remove_path(argv[0]), + filename_remove_path(argv[0])); + } + return 0; + } + /* decode the command line parameters */ + Target_Device_Object_Instance = strtol(argv[1], NULL, 0); + Target_Object_Type = strtol(argv[2], NULL, 0); + Target_Object_Instance = strtol(argv[3], NULL, 0); + Target_Object_Property = strtol(argv[4], NULL, 0); + Target_Object_Property_Priority = strtol(argv[5], NULL, 0); + Target_Object_Property_Index = strtol(argv[6], NULL, 0); + if (Target_Object_Property_Index == -1) + Target_Object_Property_Index = BACNET_ARRAY_ALL; + 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 + 1); + return 1; + } + if (Target_Object_Type > MAX_BACNET_OBJECT_TYPE) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Type, MAX_BACNET_OBJECT_TYPE + 1); + return 1; + } + if (Target_Object_Instance > BACNET_MAX_INSTANCE) { + fprintf(stderr, "object-instance=%u - it must be less than %u\r\n", + Target_Object_Instance, BACNET_MAX_INSTANCE + 1); + return 1; + } + if (Target_Object_Property > MAX_BACNET_PROPERTY_ID) { + fprintf(stderr, "object-type=%u - it must be less than %u\r\n", + Target_Object_Property, MAX_BACNET_PROPERTY_ID + 1); + return 1; + } + args_remaining = (argc - 7); + for (i = 0; i < MAX_PROPERTY_VALUES; i++) { + tag_value_arg = 7 + (i * 2); + /* special case for context tagged values */ + if (toupper(argv[tag_value_arg][0]) == 'C') { + context_tag = strtol(&argv[tag_value_arg][1], NULL, 0); + tag_value_arg++; + args_remaining--; + Target_Object_Property_Value[i].context_tag = context_tag; + Target_Object_Property_Value[i].context_specific = true; + } else { + Target_Object_Property_Value[i].context_specific = false; + } + property_tag = strtol(argv[tag_value_arg], NULL, 0); + value_string = argv[tag_value_arg + 1]; + args_remaining -= 2; + /* printf("tag[%d]=%u value[%d]=%s\r\n", + i, property_tag, i, value_string); */ + if (property_tag >= MAX_BACNET_APPLICATION_TAG) { + fprintf(stderr, "tag=%u - it must be less than %u\r\n", + property_tag, MAX_BACNET_APPLICATION_TAG); + return 1; + } + status = bacapp_parse_application_data(property_tag, + value_string, &Target_Object_Property_Value[i]); + if (!status) { + /* FIXME: show the expected entry format for the tag */ + fprintf(stderr, "unable to parse the tag value\r\n"); + return 1; + } + Target_Object_Property_Value[i].next = NULL; + if (i > 0) { + Target_Object_Property_Value[i - 1].next = + &Target_Object_Property_Value[i]; + } + if (args_remaining <= 0) + break; + } + /* setup my info */ + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + address_init(); + Init_Service_Handlers(); + /* configure standard BACnet/IP port */ + bip_set_interface("eth0"); /* for linux */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; + /* 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 = bip_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_Write_Property_Request + (Target_Device_Object_Instance, Target_Object_Type, + Target_Object_Instance, Target_Object_Property, + &Target_Object_Property_Value[0], + Target_Object_Property_Priority, + Target_Object_Property_Index); + } 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; +} diff --git a/bacnet-stack-0-3-0/demo/writeprop/makefile.b32 b/bacnet-stack-0-3-0/demo/writeprop/makefile.b32 new file mode 100644 index 00000000..02270b36 --- /dev/null +++ b/bacnet-stack-0-3-0/demo/writeprop/makefile.b32 @@ -0,0 +1,155 @@ +# +# 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 = bacwp +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;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\s_wp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\datetime.c \ + ..\..\indtext.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 diff --git a/bacnet-stack-0-3-0/dlmstp.h b/bacnet-stack-0-3-0/dlmstp.h new file mode 100644 index 00000000..c7465bdc --- /dev/null +++ b/bacnet-stack-0-3-0/dlmstp.h @@ -0,0 +1,107 @@ +/*####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 DLMSTP_H +#define DLMSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* defines specific to MS/TP */ +#define MAX_HEADER (2+1+1+1+2+1+2+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + unsigned pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void dlmstp_init(void); + void dlmstp_cleanup(void); + void dlmstp_millisecond_timer(void); + + /* returns number of bytes sent on success, negative on failure */ + int dlmstp_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 */ + + /* returns the number of octets in the PDU, or zero on failure */ + uint16_t dlmstp_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 */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames(unsigned max_info_frames); + unsigned dlmstp_max_info_frames(void); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master(uint8_t max_master); + uint8_t dlmstp_max_master(void); + + void dlmstp_set_my_address(uint8_t my_address); + void dlmstp_get_my_address(BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ + + /* MS/TP state machine functions */ + uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/doc/README.build b/bacnet-stack-0-3-0/doc/README.build new file mode 100644 index 00000000..43ae1b73 --- /dev/null +++ b/bacnet-stack-0-3-0/doc/README.build @@ -0,0 +1,9 @@ +BACnet Stack +Developer Build + +This BACnet Stack is designed as a library for an embedded product. +However, there are a number of example applications in the demo/ directory +that show how it can be used for client and server applications. + +The demos can be built using makefiles in the root directory, or by +using individual makefiles in the demo directories. \ No newline at end of file diff --git a/bacnet-stack-0-3-0/doc/README.developer b/bacnet-stack-0-3-0/doc/README.developer new file mode 100644 index 00000000..2543c7f0 --- /dev/null +++ b/bacnet-stack-0-3-0/doc/README.developer @@ -0,0 +1,103 @@ +This BACnet stack is service driven. It handles the services (BACnet requests +like WhoIs, I-Am, ReadProperty, etc) to/from the network layer to functions that +handle the application layer. There are a bunch of functions that facilitate +encoding and decoding to/from the network message data to/from something +meaningful in the program. + +A BACnet device is supposed to support, at a minimum, ReadProperty service +(server) and a single Device Object. This even applies to a BACnet client on a +PC that is used for reading other BACnet devices. + +There are a number of core files that you will need. Services such as +ReadProperty, I-Am, and Reject are consided core files. The following BACnet +services (messages) are provided by this BACnet stack files: + + * abort.c - BACnet Abort service encode/decode + * bacerror.c - BACnet Error service encode/decode + * reject.c - BACnet Reject service encode/decode + * rp.c - BACnet ReadProperty service encode/decode + * arf.c - AtomicReadFile service encode/decode + * awf.c - AtomicWriteFile service encode/decode + * rpm.c - ReadPropertyMultiple service encode/decode + * iam.c - I-Am service encode/decode + * whois.c - WhoIs service encode/decode + * wp.c - WriteProperty service encode/decode + * dcc.c - DeviceCommunicationControl service encode/decode + * ihave.c - I-Have service encode/decode + * rd.c - ReinitializedDevice service encode/decode + * timesync.c - TimeSynchronization service encode/decode + * whohas.c - WhoHas service encode/decode + +Adding additional services is a matter of adding the encoding and decoding for +the service into/from meaningful data, and I like to add unit testing, a demo +handler and send function, as well as a demo command line example. + +The BACnet stack also includes files for handling client functionality, which +requires Confirmed messages, and utilizes something called binding. Binding is a +way of acquiring a Device Object Instance's MAC address by sending a broadcast +Who-Is to that Device Object and waiting for the I-Am from that Device Object. +When the I-Am arrives, the MAC address can be stored and used to send unicast +messages to that Device Object and its member objects or properties. Here are +the files that handle BACnet binding: + + * address.c - 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 binding with the data from I-Am. This is needed for client +functionality. + * tsm.c - Transaction State Machine handles resending messages if a timeout +occurs, and is needed for client functionality. The transaction state machine is +used for Confirmed messages and segmentation. For confirmed messages, it +automatically (via tsm_timer_milliseconds) handles the retries and the timeout. +It uses the InvokeID as the unique key (although officially it should be the +combination of InvokeID, DeviceID, and service). So if you tried to send a +confirmed request to a device that was taken offline, you would see the retry go +out after every apdu_timeout until apdu retries had completed. Then the +transaction would self-delete (free). The stack as it is written (and most +stacks are written this way) has a limited amount of transactions, and if you +are sending alot of confirmed data, it can be a bottleneck if they are not freed +in a timely manner. + +This BACnet stack includes a number of example objects. The reason that they are +examples is because your device and its objects and their properties will +undoubtedly be unique to your product. The example objects in this BACnet stack +are the same and contiguous for each object represented - but this is not +required. This stack does not include an example of every type of BACnet object +or property - but have no fear! Adding a new object type is mostly just a matter +of adding all the data encoding/decoding for that object for each service and +property supported. When a new object is added, it must also add some API hooks +in the Device Object, since the Device Object contains an object list. The +example object files in the BACnet stack include: + + * demo/object/ai.c - analog input object demo + * demo/object/ao.c - analog output object demo + * demo/object/bacfile.c - File object demo + * demo/object/device.c - device object demo + * demo/object/bi.c - binary input object demo + * demo/object/bo.c - binary output object demo + * demo/object/lsp.c - life safety point demo + +The BACnet stack includes a number of core files that handle the service packets +that come in from the datalink layer. The core files include: + + * apdu.c - handles dispatching the services to the proper handlers + * bacdcode.c - primitive BACnet datatype encoding and decoding + * bacstr.c - BACnet string encoding and decoding + * bigend.c - determines if CPU is bigendian + * datalink.c - generic API for all datalink layers + * npdu.c - handles dispatching of the network message to the apdu +dispatcher. It would be where routing is handled if the stack supported more +than one physical layer. + +The DataLink Layer controls orderly access to the physical medium. The following +files are used for the datalink handling in this BACnet stack: + + * bip.c - BACnet/IP functionality - depends on bip_init.c in port/xx + * dllmstp.h - MS/TP datalink layer (see also dlmstp.c,mstp.c,crc.c) + * arcnet.h - ARCNET datalink layer functionality API + * ethernet.h - BACnet Ethernet datalink layer functionality API + +There are several demonstration applications in the demo directory, along with +several demonstation objects and handlers. All the demos accept command line +options and have been tested under Win32 and Linux. There is a makefile in the +respective demo directory for Linux and for Borland C++ compilers, and a master +makefile at the root level (Makefile=Linux, makefile.b32=Borland). diff --git a/bacnet-stack-0-3-0/doc/README.msvc b/bacnet-stack-0-3-0/doc/README.msvc new file mode 100644 index 00000000..34a1e05d --- /dev/null +++ b/bacnet-stack-0-3-0/doc/README.msvc @@ -0,0 +1,76 @@ +BACnet Stack - SourceForge.net +Build for Visual C++ 6.0 + +When building the BACnet stack using Visual C++ compiler, +there are some settings that are important. + +Q. MSVC refuses to open bacnet.dsw and bacnet.dsp. + +A. bacnet.dsw and bacnet.dsp are text files that were retrieved +from CVS on a unix client and are now in unix text file format since +they end with a "\r\n" rather than "\n". Use the unix2dos commandline +tool to convert them back to dos: +unix2dos bacnet.dsw +unix2dos bacnet.dsp + +Q. error LNK2001: unresolved external symbol _WinMain@16 + +A. The demo ports/win32/main.c was designed as a Win32 Console +Application. If you want to change it to a Windows GUI application, +you will have to add all the Windows GUI code, including WinMain(). +I recommend that you use a framework, such as WxWidgets/WxWindows, +but this has not been done yet. + +Q. error C1083: Cannot open include file: 'stdint.h': No such file + +A. The BACnet stack uses some header files, and Visual C++ needs to know +where they are: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: Preprocessor +5. You can see the "Additional include directories:" box +6. Type the path to stdint.h in that edit box (using a comma if necessary) +7. Type the path to bacdcode.h in that edit box (using a comma if necessary) +In my system, the paths look like: +c:\code\bacnet-stack\,c:\code\bacnet-stack\ports\win32\, +c:\code\bacnet-stack\demo\handler\,c:\code\bacnet-stack\demo\object\ +8. Press OK +9. Compile the project again... + +Q. error C2065: 'MAX_MPDU' : undeclared identifier + +A. The BACnet stack uses a preprocessor define to configure +its datalink layer. In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type BACDL_BIP=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol __imp__closesocket@4 + +A. Visual C++ needs to have the Winsock library to be happy: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "Link" tab (4th Tab) +4. You can see "Object/library modules:" edit box +5. Type Wsock32.LIB in that edit box +6. Press OK +7. Compile the entire project again... + +Q. error C2061: in file tsm.c +A. The BACnet stack uses a preprocessor define to configure +client functionality in the Transaction State Machine (TSM). +In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type TSM_ENABLED=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... diff --git a/bacnet-stack-0-3-0/doc/README.release b/bacnet-stack-0-3-0/doc/README.release new file mode 100644 index 00000000..e29a70cb --- /dev/null +++ b/bacnet-stack-0-3-0/doc/README.release @@ -0,0 +1,67 @@ +SourceForge Release Checklist for the BACnet Embedded Stack Project +written by Steve Karg (using a similar doc by Kim Gräsman as a guide) + +Verify that the test build is clean. Test code with clean directory. + +Get a clean build (no warnings or errors). + +The program must be functional (it works). + +Clean up the object files and binaries to prepare for tar +$ make clean + +Make the source code look the same +$ indent -kr -nut -nlp *.c *.h + +Verify that the code compiles and runs the demos without error or +warnings: +$ make all +$ demo/server 123 (etc) +Verify that the unit tests compile and pass: +$ ./unittest.sh +$ cat test.log | grep Failed + +Commit any changes to subversion. +$ svn commit +$ svn update + +Create the change log +$ svn log --xml --verbose | xsltproc svn2cl.xsl - > ChangeLog + +Set a tag on the "bacnet-stack" module called "bacnet-stack-0-0-0" +$ svn copy https://svn.sourceforge.net/svnroot/bacnet/current https://svn.sourceforge.net/svnroot/bacnet/tags/bacnet-stack-0-0-0 + +Get a clean version out of subversion that doesn't have subversion in it. +$ svn export https://svn.sourceforge.net/svnroot/bacnet/trunk/bacnet-stack bacnet-stack + +tar and gzip the clean directory from one directory up. +$ tar -cvvzf bacnet-stack-0.0.0.tgz bacnet-stack/ + +Put the new release up on SourceForge +Connect to ftp://upload.sourceforge.net/ as anonymous +Upload the tarball to the incoming directory. + +Go to http://sourceforge.net/projects/bacnet/ + Admin -> File Releases + Add Release + Release Name "bacnet-stack-0.0.0" + Set status to Hidden (for now). + Create a change log (use svn log) + [Submit/Refresh] + Attach the tarball that was uploaded (should be listed) + [Add Files and/or Refresh View] + Set file metadata + Processor: Platform Independent + Release Date: Today's date + File Type: Source .gz + [Update/Refresh] + Activate Release + Set status to Active + [Submit/Refresh] + [Send Notice] + + Update the website (if necessary) + + Add release notes under What's New on SourceForge. + The release notes should include project details + for someone unfamiliar with the project or BACnet. diff --git a/bacnet-stack-0-3-0/doc/README.sloc b/bacnet-stack-0-3-0/doc/README.sloc new file mode 100644 index 00000000..1dee67e5 --- /dev/null +++ b/bacnet-stack-0-3-0/doc/README.sloc @@ -0,0 +1,31 @@ +SLOC Directory SLOC-by-Language (Sorted) +12610 top_dir ansic=12435,sh=175 +8317 ports ansic=8317 +7762 demo ansic=7762 +187 test ansic=187 +0 doc (none) +0 license (none) +0 utils (none) + + +Totals grouped by language (dominant language first): +ansic: 28701 (99.39%) +sh: 175 (0.61%) + + + + +Total Physical Source Lines of Code (SLOC) = 28,876 +Development Effort Estimate, Person-Years (Person-Months) = 6.83 (81.99) + (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05)) +Schedule Estimate, Years (Months) = 1.11 (13.34) + (Basic COCOMO model, Months = 2.5 * (person-months**0.38)) +Estimated Average Number of Developers (Effort/Schedule) = 6.15 +Total Estimated Cost to Develop = $ 923,008 + (average salary = $56,286/year, overhead = 2.40). +SLOCCount, Copyright (C) 2001-2004 David A. Wheeler +SLOCCount is Open Source Software/Free Software, licensed under the GNU GPL. +SLOCCount comes with ABSOLUTELY NO WARRANTY, and you are welcome to +redistribute it under certain conditions as specified by the GNU GPL license; +see the documentation for details. +Please credit this data as "generated using David A. Wheeler's 'SLOCCount'." diff --git a/bacnet-stack-0-3-0/doc/code-standard.txt b/bacnet-stack-0-3-0/doc/code-standard.txt new file mode 100644 index 00000000..4045bbad --- /dev/null +++ b/bacnet-stack-0-3-0/doc/code-standard.txt @@ -0,0 +1,221 @@ +This software runs on many platforms, and can be compiled with a number of +different compilers; here are some rules for writing code that will work +on multiple platforms. + +Regarding tabs, indenting, and code style: we run "indent -kr -nut -nlp" +on the code prior to releasing it. This ensures a standard look and feel +to the code regardless of the authors preferred style. You may certainly +adjust the code to your preferred style using the indent tool. We use the +script indent.sh to adjust all the .c and .h files. + +Don't use C++-style comments (comments beginning with "//" and running +to the end of the line) for modules that are written in C. The module +may run through C rather than C++ compilers, and not all C compilers +support C++-style comments (GCC does, but IBM's C compiler for AIX, for +example, doesn't do so by default). Note: there is an application +called usr/bin/ccmtcnvt in the liwc package that converts the C++ +comments to C comments. There is a script utilizing ccmtcnvt called +comment.sh created for this project that searches all the c and h files +for C++ headers and converts them. + +Don't initialize variables in their declaration with non-constant +values. Not all compilers support this. E.g. don't use + uint32_t i = somearray[2]; +use + uint32_t i; + i = somearray[2]; +instead. + +Don't use zero-length arrays; not all compilers support them. If an +array would have no members, just leave it out. + +Don't declare variables in the middle of executable code; not all C +compilers support that. Variables should be declared at the beginning +of a function or compound statement, or outside a function + +Don't use "inline"; not all compilers support it. + +Use the C99 stdint.h and stdbool.h definitions for declaring variables +when needed. If they are not defined for your compiler, put those files +into the ports directory for your compiler with the proper definitions. +Sometimes scalable code should just use an int or unsigned declaration. +8-bit unsigned = uint8_t +8-bit signed = int8_t +16-bit unsigned = uint16_t +16-bit signed = int16_t +32-bit unsigned = uint32_t +32-bit signed = int32_t +boolean = bool + +Don't use "long" to mean "signed 32-bit integer", and don't use +"unsigned long" to mean "unsigned 32-bit integer"; "long"s are 64 bits +long on many platforms. Use "int32_t" for signed 32-bit integers and use +"uint32_t" for unsigned 32-bit integers. + +Don't use "long" to mean "signed 64-bit integer" and don't use "unsigned +long" to mean "unsigned 64-bit integer"; "long"s are 32 bits long on +many other platforms. Don't use "long long" or "unsigned long long", +either, as not all platforms support them; use "int64_t" or "uint64_t", +which need to be defined as the appropriate types for 64-bit signed and +unsigned integers. + +Don't use a label without a statement following it. For example, +something such as + + if (...) { + + ... + + done: + } + +will not work with all compilers - you have to do + + if (...) { + + ... + + done: + ; + } + +with some statements, even if it's a null statement, after the label. + +Don't use "bzero()", "bcopy()", or "bcmp()"; instead, use the ANSI C +routines + + "memset()" (with zero as the second argument, so that it sets + all the bytes to zero); + + "memcpy()" or "memmove()" (note that the first and second + arguments to "memcpy()" are in the reverse order to the + arguments to "bcopy()"; note also that "bcopy()" is typically + guaranteed to work on overlapping memory regions, while + "memcpy()" isn't, so if you may be copying from one region to a + region that overlaps it, use "memmove()", not "memcpy()" - but + "memcpy()" might be faster as a result of not guaranteeing + correct operation on overlapping memory regions); + + and "memcmp()" (note that "memcmp()" returns 0, 1, or -1, doing + an ordered comparison, rather than just returning 0 for "equal" + and 1 for "not equal", as "bcmp()" does). + +Not all platforms necessarily have "bzero()"/"bcopy()"/"bcmp()", and +those that do might not declare them in the header file on which they're +declared on your platform. + +Don't use "index()" or "rindex()"; instead, use the ANSI C equivalents, +"strchr()" and "strrchr()". Not all platforms necessarily have +"index()" or "rindex()", and those that do might not declare them in the +header file on which they're declared on your platform. + +Don't fetch data from packets by getting a pointer to data in the +packet, casting that pointer to a pointer to a structure, +and dereferencing that pointer. That pointer won't necessarily be aligned +on the proper boundary, which can cause crashes on some platforms (even +if it doesn't crash on an x86-based PC). This means that you cannot +safely cast it to any data type other than a pointer to "char", +"unsigned char", "uint8_t", or other one-byte data types. You cannot, +for example, safely cast it to a pointer to a structure, and then access +the structure members directly; on some systems, unaligned accesses to +integral data types larger than 1 byte, and floating-point data types, +cause a trap, which will, at best, result in the OS slowly performing an +unaligned access for you, and will, on at least some platforms, cause +the program to be terminated. + +The data in a packet is not necessarily in the byte order of +the machine on which this software is running. Make use of +big_endian() which returns non-zero on big_endian machines. + +Use "ntohs()", "ntohl()", "htons()", or "htonl()" only in the ports +directories since the header files required to define or declare +them differ between platforms. There are some common functions in +the bacdcode library for converting to and from long and short. + +Don't put a comma after the last element of an enum - some compilers may +either warn about it (producing extra noise) or refuse to accept it. + +When opening a file with "fopen()", "freopen()", or "fdopen()", if the +file contains ASCII text, use "r", "w", "a", and so on as the open mode +- but if it contains binary data, use "rb", "wb", and so on. On +Windows, if a file is opened in a text mode, writing a byte with the +value of octal 12 (newline) to the file causes two bytes, one with the +value octal 15 (carriage return) and one with the value octal 12, to be +written to the file, and causes bytes with the value octal 15 to be +discarded when reading the file (to translate between C's UNIX-style +lines that end with newline and Windows' DEC-style lines that end with +carriage return/line feed). + +In addition, that also means that when opening or creating a binary +file, you must use "open()" (with O_CREAT and possibly O_TRUNC if the +file is to be created if it doesn't exist), and OR in the O_BINARY flag. +That flag is not present on most, if not all, UNIX systems, so you must +also do + + #ifndef O_BINARY + #define O_BINARY 0 + #endif + +to properly define it for UNIX (it's not necessary on UNIX). + +Don't use forward declarations of static arrays without a specified size +in a fashion such as this: + + static const value_string foo_vals[]; + + ... + + static const value_string foo_vals[] = { + { 0, "Red" }, + { 1, "Green" }, + { 2, "Blue" }, + { 0, NULL } + }; + +as some compilers will reject the first of those statements. Instead, +initialize the array at the point at which it's first declared, so that +the size is known. + +Don't put declarations in the middle of a block; put them before all +code. Not all compilers support declarations in the middle of code, +such as + + int i; + + i = foo(); + + int j; + +For #define names and enum member names, prefix the names with a tag so +as to avoid collisions with other names - this might be more of an issue +on Windows, as it appears to #define names such as DELETE and +OPTIONAL. + +Don't use "variadic macros", such as + + #define DBG(format, args...) fprintf(stderr, format, ## args) + +as not all C compilers support them. Use macros that take a fixed +number of arguments, such as + + #define DBG0(format) fprintf(stderr, format) + #define DBG1(format, arg1) fprintf(stderr, format, arg1) + #define DBG2(format, arg1, arg2) fprintf(stderr, format, arg1, arg2) + + ... + +or something such as + + #define DBG(args) printf args + +Instead of tmpnam(), use mkstemp(). tmpnam is insecure and should +not be used any more. Note: mkstemp does not accept NULL as a parameter. + +Try to write code portably whenever possible, however; note that +there are some routines in the software that are platform-dependent +implementations. The platform independent API is declared in the +header file, and the dependent routine is placed in a ports directory. + +Reference: The cross platform aspect of this coding standard is based +on the developer coding standard for Ethereal and has been modified +by Steve Karg for this project. Thank you, Ethereal! diff --git a/bacnet-stack-0-3-0/doc/htdocs/images/BACnet.png b/bacnet-stack-0-3-0/doc/htdocs/images/BACnet.png new file mode 100644 index 00000000..29c7b57d Binary files /dev/null and b/bacnet-stack-0-3-0/doc/htdocs/images/BACnet.png differ diff --git a/bacnet-stack-0-3-0/doc/htdocs/index.html b/bacnet-stack-0-3-0/doc/htdocs/index.html new file mode 100644 index 00000000..aa528b68 --- /dev/null +++ b/bacnet-stack-0-3-0/doc/htdocs/index.html @@ -0,0 +1,483 @@ + + + + + BACnet stack - open source BACnet protocol stack + + +

BACnet Stack

+

An open source BACnet protocol stack for embedded systems

+
+

About this Project

+

This BACnet protocol stack library + provides a BACnet application layer, network layer and media access (MAC) + layer communications services for an embedded system.

+ +

BACnet - A Data Communication Protocol for Building + Automation and Control Networks - see bacnet.org. BACnet is a standard data + communication protocol for Building Automation and Control Networks. BACnet + is an open protocol, which means anyone can contribute to the standard, and + anyone may use it. The only caveat is that the BACnet standard document + itself is copyrighted by ASHRAE, and they sell the document to help defray + costs of developing and maintaining the standard (just like IEEE or ANSI or + ISO).

+ +

For software developers, the BACnet protocol is a standard way to send and + receive messages on the wire containing data that is understood by other + BACnet compliant devices. The BACnet standard defines a standard way to + communicate over a number of wires, known as Data Link/Physical Layers: + Ethernet, EIA-485, EIA-232, ARCNET, and LonTalk. The BACnet standard also + defines a standard way to communicate using UDP, IP and HTTP (Web + Services).

+ + There are other open source projects for BACnet:

+
    +
  • VTS - visual test shell for + Win32, used for Visually testing a BACnet implementation. It also includes + a detailed network sniffer for BACnet messages, and the ability to send + any BACnet services. The source code is in the public domain.
  • +
  • Wireshark - an open source, + cross platform protocol analyzer with BACnet support. The detailed BACnet + support in began in version 0.10.11 released on May 4, 2005 when Wireshark + was known as Ethereal.
  • +
  • BACnet4Linux - a + LGPL BACnet application that requires Linux as the OS.
  • +
  • BACnet Firewall Router -an + application that combines BACnet routing capability with traffic management + functions to carefully control access to building automation and control + networks.
  • +
  • BACpypes - a + BACnet stack written in Python.
  • +
+ +

There are also commercial BACnet protocol source code libraries for BACnet + that are designed for embedded use:

+
    +
  • CimetricsTM - has a source library + called BACstac/32 as part of their BACNet Protocol Stack SDK.
  • +
  • Polarsoft - has a + protocol stack source library for embedded use called FreeRangeTM and PolarSoft® FreeRange VSB (Very + Small BACnet stack).
  • +
  • SCADA Engine - The BACnet + Linux Server is a complete BACnet Device running on the linux platform. + The entire source code is available for custom applications and has been + written in ANSI C which has been succesfully ported to Unix, + VxWorks etc.
  • +
+ +

Licensing

+

Our BACnet protocol stack implementation is specifically designed for the + embedded BACnet appliance, using a GPL with exception license (like eCos), + which means that any changes to the core code that are distributed are + made available, but the BACnet library can be linked to + proprietary code without it becoming GPL. + See the eCos license overview for + easy to read details about this exception to the GPL. + The license does not require users to release the source code of any + applications that are developed with this BACnet stack.

+ +

The text of the GPL exception included in each source file is as + follows:

+ +
+

"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."

+

+ +

The source code

+

The source code is written in C for portability, and includes + unit tests (PC based unit tests) and example application code. + Since the code is designed to be + portable, it compiles with GCC as well as other compilers, + such as Borland C++, Visual C++, MinGW, or MicroChip C18. + The source code is also designed to be readable, understandable, + and most importantly, easy to use.

+ +

The BACnet protocol is an ASHRAE/ANSI/ISO standard, so this library + adheres to that standard. BACnet has no royalties or licensing restrictions, + and registration for a BACnet + vendor ID is free.

+ +

What the code does

+ +

The stack comes with unit tests and demo applications + that can be run under Linux, Win32, RTOS-32, or just about any embedded + microcontroller.

+ +

The unit tests can be run in a command shell on Linux using the + unittest.sh script, or using individual .mak files. They were tested under + Debian GNU/Linux and Ubuntu Linux.

+ +

The BACnet stack was functionally tested + using VTS (Visual Test Shell), + another project hosted on SourceForge, as well as various BACnet controllers, + BACnet workstations, and through BACnet routers.

+ +

Using a master Makefile in the project root directory, a dozen + demo applications can be created that run under Linux or Win32. + Linux supports BACnet Ethernet, BACnet/IP, or ARCNET data link layer + for communication, and BACnet/IP is supported under Win32. BACnet Ethernet + can also be used under Win32 with the WinPcap library. + Root priveleges are required to run the Ethernet or ARCNET interfaces + on Linux, but not needed to run BACnet/IP.

+ + + $ make clean all
+ $ demo/server/bacsrv 123
+ BACnet Server Demo - Device #123
+
+ $ demo/readprop/bacrp
+ bacrp device-instance object-type object-instance property [index]
+
+ $ demo/writeprop/bacwp
+ bacwp device-instance object-type object-instance property tag value [priority] [index]
+
+ $ demo/readfile/bacarf
+ bacarf device-instance file-instance local-name
+
+ $ demo/writefile/bacawf
+ bacawf device-instance file-instance local-name
+
+ $ demo/reinit/bacrd
+ Usage: bacrd device-instance state [password]
+ Send BACnet ReinitializeDevice service to device.
+
+ $ demo/whohas/bacwh
+ Usage: bacwh object-type object-instance | object-name
+ Send BACnet WhoHas request to devices, and wait for responses.
+
+ $ demo/dcc/bacdcc
+ Usage: bacdcc device-instance state timeout [password]
+ Send BACnet DeviceCommunicationControl service to device.
+
+ $ demo/timesync/bacts
+ Received TimeSyncronization Request
+ 2006/8/30 07:10:45.00
+
+ $ demo/ucov/bacucov
+ Usage: bacucov pid device-id object-type object-instance time property tag value [priority] [index]
+
+ $ demo/whois/bacwi
+ Usage: bacwi device-instance | device-instance-min device-instance-max
+ Send BACnet WhoIs request to devices, and wait for responses.
+
+ The device-instance can be 0 to 4194303, or -1 for ALL.
+ The device-instance can also be specified as a range.
+
+
+ +

The demos can be compiled under Win32 using Borland C++ or + Microsoft Visual C++, both of which + are free (as in beer) command line compilers. Be sure to pick up the free + patches (service packs) for the Borland C++ compiler (SP1, SP2), as well as the free turbo debugger. It might also be possible to create Win32 projects using the free Visual Studio Express Edition or MinGW - Minimalist GNU for Windows, but I have haven't tried it. I have used Code::Blocks for compiling the unit tests using the MinGW compiler, but have not tried to get the demos working with MinGW. I have used Code::Blocks with the Borland C++ compiler and it successfully compiles the code.

+ +

There is a Makefile in the ports/rtos32 directory, and a sample + application that runs under RTOS-32. + It currently uses the BACnet/IP data link layer for communication, and also + has an MS/TP datalink layer sample application. + It compiles using Borland C++.

+ +

There is a project in the ports/pic18 directory, and a sample + application that can be build using MP-Lab + and the Microchip compiler MCC18. The MS/TP portion is now working, + and had a good showing at the BACnet International plugfest.

+ + + +

BACnet services supported matrix

+

The BACnet stack currently implements the following services listed in the + the table. We plan to add the rest of the services as we go. + With the services that are implemented, you could build a BACnet device + that meets the standardized profile for a BACnet Smart Sensor, + BACnet Smart Actuator, or a BACnet Application Specific Controller.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
BACnet ServiceInitiateExecute
Who IsYesYes
I AmYesYes
Who HasYesYes
I HaveYesYes
Read PropertyYesYes
Write PropertyYesYes
Device Communication ControlYesYes
ReinitializeDeviceYesYes
Atomic Read FileYesYes
Atomic Write FileYesYes
Time SynchronizationYesYes
UTC Time SynchronizationYesYes
Subscribe COV--
Confirmed COV NotificationYesYes
Unconfirmed COV NotificationYesYes
Read Property Multiple--
Read Property Conditional--
Read Range--
Write Property Multiple--
Get Alarm Summary--
Get Event Information--
Get Enrollment Summary--
Acknowledge Alarm--
Confirmed Event Notification--
Unconfirmed Event Notification--
Unconfirmed Text Message--
Confirmed Text Message--
Add List Element--
Remove List Element--
Create Object--
Delete Object--
Unconfirmed Private Transfer--
Confirmed Private Transfer--
VT Open--
VT Data--
VT Close--
+ +

BACnet Objects

+ +

The BACnet stack currently implements a Device Object, and + handles all of the ReadProperty inquiries for the required + Device Object properties. The stack handles Who-Is inquiries + with an I-Am, WhoHas with I-Have, and handles reject messages for + services not currently supported. There is built in handling for + DeviceCommunicationControl.

+ +

The example handlers interact with example objects, including + Analog Input, Analog Output, Analog Value, Binary Input, + Binary Output, Binary Value, Life Safety Point, + and Multi-state Output objects. + The objects can be accessed using WriteProperty, + ReadProperty, or Who-Has services. Adding other BACnet objects + is only a matter of setting up ReadProperty, WriteProperty, or + I-Have handling for the required properties and any other + properties that you want to support.

+ +

File Objects are conditionally included in the demonstation + applications. The files can be access using WriteProperty, + ReadProperty, Who-Has, AtomicWriteFile, or AtomicReadFile services.

+ +

Getting Involved

+ +

If you want to help out on this project, join the developers mailing list, introduce yourself, and tell us what you would like to do. + If you are trying to implement a BACnet device or service using this project, + you are welcome to join the developers mailing list as well.

+ +

More details about the project can be found on + the BACnet Source Forge Project Page

+ +

There is documentation that describes the mechanisms in the BACnet Stack. + I wrote up some answers to some frequently asked questions. Of course, + there are a handful of text files in the doc directory of the project.

+ +

BACnet + Stack file download

+ +

BACnet Developer Resources

+ +There are a number of resources that can help you develop a BACnet product or project. + +
    +
  • VTS - visual test shell for + Win32, used for Visually testing a BACnet implementation. It also includes + a detailed network sniffer for BACnet messages, and the ability to send + any BACnet services. The source code is in the public domain.
  • +
  • Wireshark - an open source, + cross platform protocol analyzer with BACnet support. The detailed BACnet + support in began in version 0.10.11 released on May 4, 2005 when Wireshark + was known as Ethereal.
  • +
  • Ubuntu Linux or + Debian Linux - my + development platforms of choice. + Linux makes a great development platform + because all the necessary development tools are included.
  • +
  • Code::Blocks - a free cross-platform + open source C/C++ IDE. Includes the MinGW compiler for Win32.
  • +
+ +

BACnet Developer Help

+ +

BACnet® International Developer Resources

+ +

Products and Projects that use this BACnet Stack

+ +

Did you develop a product using this BACnet stack? Let us know, and you +can get a little recognition for your hard work!

+ +

SCInterface™ = Sensor Control Interface - middleware + platform for managing legacy and modern-day sensors through a centralized + interface

+ +

BACnetSim - a +portable implementation of the BACnet data communication protocol. +BACnetSim is meant for embedded devices and use MS/TP as the media +access layer. BACnetSim is a fork of bacnet-stack-0.0.1

+ +
+ + SourceForge.net Logo + +

ASHRAE® and + BACnet® are registered trademarks of the American + Society of Heating, Refrigerating and Air-Conditioning Engineers, Inc., + 1791 Tullie Circle NE, Atlanta, GA 30329.

+

Updated 24-Oct-2006 by Steve Karg

+ + diff --git a/bacnet-stack-0-3-0/ethernet.h b/bacnet-stack-0-3-0/ethernet.h new file mode 100644 index 00000000..12ed8632 --- /dev/null +++ b/bacnet-stack-0-3-0/ethernet.h @@ -0,0 +1,76 @@ +/*####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 ETHERNET_H +#define ETHERNET_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* specific defines for Ethernet */ +#define MAX_HEADER (6+6+2+1+1+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool ethernet_valid(void); + void ethernet_cleanup(void); + bool ethernet_init(char *interface_name); + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ + int ethernet_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 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ + uint16_t ethernet_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 ethernet_set_my_address(BACNET_ADDRESS * my_address); + void ethernet_get_my_address(BACNET_ADDRESS * my_address); + void ethernet_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/filename.c b/bacnet-stack-0-3-0/filename.c new file mode 100644 index 00000000..449ad3f8 --- /dev/null +++ b/bacnet-stack-0-3-0/filename.c @@ -0,0 +1,101 @@ +/*####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 +#include + +char *filename_remove_path(const char *filename_in) +{ + char *filename_out = NULL; + + /* allow the device ID to be set */ + if (filename_in) { + filename_out = strrchr(filename_in, '\\'); + if (!filename_out) + filename_out = strrchr(filename_in, '/'); + /* go beyond the slash */ + if (filename_out) + filename_out++; + } + + return filename_out; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void testFilename(Test * pTest) +{ + char *data1 = "c:\\Joshua\\run"; + char *data2 = "/home/Anna/run"; + char *data3 = "c:\\Program Files\\Christopher\\run.exe"; + char *data4 = "//Mary/data/run"; + char *filename = NULL; + + filename = filename_remove_path(data1); + ct_test(pTest, strcmp("run", filename) == 0); + filename = filename_remove_path(data2); + ct_test(pTest, strcmp("run", filename) == 0); + filename = filename_remove_path(data3); + ct_test(pTest, strcmp("run.exe", filename) == 0); + filename = filename_remove_path(data4); + ct_test(pTest, strcmp("run", filename) == 0); + + return; +} + +#ifdef TEST_FILENAME +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("filename remove path", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testFilename); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_FILENAME */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/filename.h b/bacnet-stack-0-3-0/filename.h new file mode 100644 index 00000000..38ad0cd6 --- /dev/null +++ b/bacnet-stack-0-3-0/filename.h @@ -0,0 +1,46 @@ +/*####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 FILENAME_H +#define FILENAME_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + char *filename_remove_path(const char *filename_in); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/filename.ide b/bacnet-stack-0-3-0/filename.ide new file mode 100644 index 00000000..38d3cdf8 Binary files /dev/null and b/bacnet-stack-0-3-0/filename.ide differ diff --git a/bacnet-stack-0-3-0/filename.mak b/bacnet-stack-0-3-0/filename.mak new file mode 100644 index 00000000..064b4df9 --- /dev/null +++ b/bacnet-stack-0-3-0/filename.mak @@ -0,0 +1,32 @@ +#Makefile to build filename tests +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -g +CFLAGS = -Wall -I. -Itest -DTEST -DTEST_FILENAME -g + +SRCS = filename.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = filename + +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 + diff --git a/bacnet-stack-0-3-0/iam.c b/bacnet-stack-0-3-0/iam.c new file mode 100755 index 00000000..5ae007fe --- /dev/null +++ b/bacnet-stack-0-3-0/iam.c @@ -0,0 +1,281 @@ +/*####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 +#include "bacenum.h" +#include "bacdef.h" +#include "npdu.h" +#include "dcc.h" +#include "datalink.h" +#include "device.h" +#include "bacdcode.h" +#include "address.h" + +/* encode I-Am service */ +int iam_encode_apdu(uint8_t * apdu, + uint32_t device_id, + unsigned max_apdu, int segmentation, uint16_t vendor_id) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_I_AM; /* service choice */ + apdu_len = 2; + len = encode_tagged_object_id(&apdu[apdu_len], + OBJECT_DEVICE, device_id); + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], max_apdu); + apdu_len += len; + len = encode_tagged_enumerated(&apdu[apdu_len], segmentation); + apdu_len += len; + len = encode_tagged_unsigned(&apdu[apdu_len], vendor_id); + apdu_len += len; + } + + return apdu_len; +} + +int iam_decode_service_request(uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, int *pSegmentation, uint16_t * pVendor_id) +{ + int len = 0; + int apdu_len = 0; /* total length of the apdu, return value */ + int object_type = 0; /* should be a Device Object */ + uint32_t object_instance = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; + int decoded_integer = 0; + + /* OBJECT ID - object id */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_OBJECT_ID) + return -1; + len = + decode_object_id(&apdu[apdu_len], &object_type, &object_instance); + apdu_len += len; + if (object_type != OBJECT_DEVICE) + return -1; + if (pDevice_id) + *pDevice_id = object_instance; + /* MAX APDU - unsigned */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value); + apdu_len += len; + if (pMax_apdu) + *pMax_apdu = decoded_value; + /* Segmentation - enumerated */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_ENUMERATED) + return -1; + len = decode_enumerated(&apdu[apdu_len], len_value, &decoded_integer); + apdu_len += len; + if (decoded_integer >= MAX_BACNET_SEGMENTATION) + return -1; + if (pSegmentation) + *pSegmentation = decoded_integer; + /* Vendor ID - unsigned16 */ + len = + decode_tag_number_and_value(&apdu[apdu_len], &tag_number, + &len_value); + apdu_len += len; + if (tag_number != BACNET_APPLICATION_TAG_UNSIGNED_INT) + return -1; + len = decode_unsigned(&apdu[apdu_len], len_value, &decoded_value); + apdu_len += len; + if (decoded_value > 0xFFFF) + return -1; + if (pVendor_id) + *pVendor_id = (uint16_t) decoded_value; + + return apdu_len; +} + +int iam_send(uint8_t * buffer) +{ + int len = 0; + int pdu_len = 0; + BACNET_ADDRESS dest; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + /* if we are forbidden to send, don't send! */ + if (!dcc_communication_enabled()) + return 0; + + /* I-Am 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(&buffer[0], &dest, NULL, &npdu_data); + /* encode the APDU portion of the packet */ + len = iam_encode_apdu(&buffer[pdu_len], + Device_Object_Instance_Number(), + MAX_APDU, SEGMENTATION_NONE, Device_Vendor_Identifier()); + pdu_len += len; + /* send data */ + bytes_sent = datalink_send_pdu(&dest, &npdu_data, &buffer[0], pdu_len); + + return bytes_sent; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int iam_decode_apdu(uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, int *pSegmentation, uint16_t * pVendor_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + /* valid data? */ + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST) + return -1; + if (apdu[1] != SERVICE_UNCONFIRMED_I_AM) + return -1; + apdu_len = iam_decode_service_request(&apdu[2], + pDevice_id, pMax_apdu, pSegmentation, pVendor_id); + + return apdu_len; +} + +void testIAm(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + uint32_t device_id = 42; + unsigned max_apdu = 480; + int segmentation = SEGMENTATION_NONE; + uint16_t vendor_id = 42; + uint32_t test_device_id = 0; + unsigned test_max_apdu = 0; + int test_segmentation = 0; + uint16_t test_vendor_id = 0; + + len = iam_encode_apdu(&apdu[0], + device_id, max_apdu, segmentation, vendor_id); + ct_test(pTest, len != 0); + + len = iam_decode_apdu(&apdu[0], + &test_device_id, + &test_max_apdu, &test_segmentation, &test_vendor_id); + + ct_test(pTest, len != -1); + ct_test(pTest, test_device_id == device_id); + ct_test(pTest, test_vendor_id == vendor_id); + ct_test(pTest, test_max_apdu == max_apdu); + ct_test(pTest, test_segmentation == segmentation); +} + +#ifdef TEST_IAM +/* 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; +} + +void datalink_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + (void) dest; +} + +uint16_t Device_Vendor_Identifier(void) +{ + return 0; +} + +uint32_t Device_Object_Instance_Number(void) +{ + return 0; +} + +void address_add_binding(uint32_t device_id, + unsigned max_apdu, BACNET_ADDRESS * src) +{ + (void) device_id; + (void) max_apdu; + (void) src; +} + +/* dummy for apdu dependency */ +void tsm_free_invoke_id(uint8_t invokeID) +{ + /* dummy stub for testing */ + (void) invokeID; +} + + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet I-Am", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testIAm); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_IAM */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/iam.h b/bacnet-stack-0-3-0/iam.h new file mode 100644 index 00000000..3c5674da --- /dev/null +++ b/bacnet-stack-0-3-0/iam.h @@ -0,0 +1,67 @@ +/*####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 IAM_H +#define IAM_H + +#include +#include +#include "bacdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int iam_encode_apdu(uint8_t * apdu, + uint32_t device_id, + unsigned max_apdu, int segmentation, uint16_t vendor_id); + + int iam_decode_service_request(uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, int *pSegmentation, uint16_t * pVendor_id); + + int iam_send(uint8_t * buffer); + +#ifdef TEST +#include "ctest.h" + int iam_decode_apdu(uint8_t * apdu, + uint32_t * pDevice_id, + unsigned *pMax_apdu, int *pSegmentation, uint16_t * pVendor_id); + + void testIAm(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/iam.mak b/bacnet-stack-0-3-0/iam.mak new file mode 100755 index 00000000..55a783f6 --- /dev/null +++ b/bacnet-stack-0-3-0/iam.mak @@ -0,0 +1,37 @@ +#Makefile to build test case +CC = gcc +BASEDIR = . +# -g for debugging with gdb +DEFINES = -DBACFILE=1 -DTEST -DTEST_IAM -DBACDL_TEST -DBIG_ENDIAN=0 +INCLUDES = -I. -Idemo/object -Itest +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + npdu.c \ + apdu.c \ + dcc.c \ + iam.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = iam + +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 diff --git a/bacnet-stack-0-3-0/ihave.c b/bacnet-stack-0-3-0/ihave.c new file mode 100644 index 00000000..ed660ffa --- /dev/null +++ b/bacnet-stack-0-3-0/ihave.c @@ -0,0 +1,196 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "ihave.h" + +int ihave_encode_apdu(uint8_t * apdu, BACNET_I_HAVE_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_I_HAVE; + apdu_len = 2; + /* deviceIdentifier */ + len = encode_tagged_object_id(&apdu[apdu_len], + data->device_id.type, data->device_id.instance); + apdu_len += len; + /* objectIdentifier */ + len = encode_tagged_object_id(&apdu[apdu_len], + data->object_id.type, data->object_id.instance); + apdu_len += len; + /* objectName */ + len = encode_tagged_character_string(&apdu[apdu_len], + &data->object_name); + apdu_len += len; + } + + return apdu_len; +} + +/* decode the service request only */ +int ihave_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_I_HAVE_DATA * data) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + int decoded_type = 0; /* for decoding */ + + if (apdu_len && data) { + /* deviceIdentifier */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { + len += decode_object_id(&apdu[len], &decoded_type, + &data->device_id.instance); + data->device_id.type = decoded_type; + } else + return -1; + /* objectIdentifier */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number == BACNET_APPLICATION_TAG_OBJECT_ID) { + len += decode_object_id(&apdu[len], &decoded_type, + &data->object_id.instance); + data->object_id.type = decoded_type; + } else + return -1; + /* objectName */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + len += decode_character_string(&apdu[len], len_value, + &data->object_name); + } else + return -1; + } else + return -1; + + return len; +} + +int ihave_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_I_HAVE_DATA * data) +{ + int len = 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 -1; + if (apdu[1] != SERVICE_UNCONFIRMED_I_HAVE) + return -1; + len = ihave_decode_service_request(&apdu[2], apdu_len - 2, data); + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testIHaveData(Test * pTest, BACNET_I_HAVE_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_I_HAVE_DATA test_data; + + len = ihave_encode_apdu(&apdu[0], data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = ihave_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.device_id.type == data->device_id.type); + ct_test(pTest, test_data.device_id.instance == + data->device_id.instance); + ct_test(pTest, test_data.object_id.type == data->object_id.type); + ct_test(pTest, test_data.object_id.instance == + data->object_id.instance); + ct_test(pTest, characterstring_same(&test_data.object_name, + &data->object_name)); +} + +void testIHave(Test * pTest) +{ + BACNET_I_HAVE_DATA data; + + characterstring_init_ansi(&data.object_name, "Patricia - my love!"); + data.device_id.type = OBJECT_DEVICE; + for (data.device_id.instance = 1; + data.device_id.instance <= BACNET_MAX_INSTANCE; + data.device_id.instance <<= 1) { + for (data.object_id.type = OBJECT_ANALOG_INPUT; + data.object_id.type <= MAX_BACNET_OBJECT_TYPE; + data.object_id.type++) { + for (data.object_id.instance = 1; + data.object_id.instance <= BACNET_MAX_INSTANCE; + data.object_id.instance <<= 1) { + testIHaveData(pTest, &data); + } + } + } +} + +#ifdef TEST_I_HAVE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet I-Have", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testIHave); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/ihave.h b/bacnet-stack-0-3-0/ihave.h new file mode 100644 index 00000000..827815d4 --- /dev/null +++ b/bacnet-stack-0-3-0/ihave.h @@ -0,0 +1,67 @@ +/*####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 IHAVE_H +#define IHAVE_H + +#include +#include +#include "bacstr.h" + +typedef struct BACnet_I_Have_Data { + BACNET_OBJECT_ID device_id; + BACNET_OBJECT_ID object_id; + BACNET_CHARACTER_STRING object_name; +} BACNET_I_HAVE_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int ihave_encode_apdu(uint8_t * apdu, BACNET_I_HAVE_DATA * data); + + int ihave_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_I_HAVE_DATA * data); + + int ihave_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_I_HAVE_DATA * data); + +#ifdef TEST +#include "ctest.h" + void testIHave(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ihave.mak b/bacnet-stack-0-3-0/ihave.mak new file mode 100644 index 00000000..f3325ed5 --- /dev/null +++ b/bacnet-stack-0-3-0/ihave.mak @@ -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_I_HAVE -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + ihave.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = ihave + +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 diff --git a/bacnet-stack-0-3-0/indent.sh b/bacnet-stack-0-3-0/indent.sh new file mode 100755 index 00000000..ec58d1ea --- /dev/null +++ b/bacnet-stack-0-3-0/indent.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# indent uses a local indent.pro file if it exists +# echo "-kr -nut -nlp" > .indent.pro + +directory=${1-`pwd`} +for filename in $( find $directory -name '*.c' ) +do + echo Fixing DOS/Unix $filename + dos2unix $filename + echo Indenting $filename + indent -kr -nut -nlp $filename +done + +for filename in $( find $directory -name '*.h' ) +do + echo Fixing DOS/Unix $filename + dos2unix $filename + echo Indenting $filename + indent -kr -nut -nlp $filename +done + +for filename in $( find $directory -name '*~' ) +do + echo Removing backup $filename + rm $filename +done + diff --git a/bacnet-stack-0-3-0/indtext.c b/bacnet-stack-0-3-0/indtext.c new file mode 100644 index 00000000..02cd41c6 --- /dev/null +++ b/bacnet-stack-0-3-0/indtext.c @@ -0,0 +1,228 @@ +/*####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 +#include +#include "indtext.h" + +#if defined(__BORLANDC__) || defined(_MSC_VER) +#define strcasecmp stricmp +#endif + +bool indtext_by_string(INDTEXT_DATA * data_list, + const char *search_name, unsigned *found_index) +{ + bool found = false; + unsigned index = 0; + + if (data_list && search_name) { + while (data_list->pString) { + if (strcmp(data_list->pString, search_name) == 0) { + index = data_list->index; + found = true; + break; + } + data_list++; + } + } + + if (found && found_index) + *found_index = index; + + return found; +} + +/* case insensitive version */ +bool indtext_by_istring(INDTEXT_DATA * data_list, + const char *search_name, unsigned *found_index) +{ + bool found = false; + unsigned index = 0; + + if (data_list && search_name) { + while (data_list->pString) { + if (strcasecmp(data_list->pString, search_name) == 0) { + index = data_list->index; + found = true; + break; + } + data_list++; + } + } + + if (found && found_index) + *found_index = index; + + return found; +} + +unsigned indtext_by_string_default(INDTEXT_DATA * data_list, + const char *search_name, unsigned default_index) +{ + unsigned index = 0; + + if (!indtext_by_string(data_list, search_name, &index)) + index = default_index; + + return index; +} + +unsigned indtext_by_istring_default(INDTEXT_DATA * data_list, + const char *search_name, unsigned default_index) +{ + unsigned index = 0; + + if (!indtext_by_istring(data_list, search_name, &index)) + index = default_index; + + return index; +} + +const char *indtext_by_index_default(INDTEXT_DATA * data_list, + unsigned index, const char *default_string) +{ + const char *pString = NULL; + + if (data_list) { + while (data_list->pString) { + if (data_list->index == index) { + pString = data_list->pString; + break; + } + data_list++; + } + } + + return pString ? pString : default_string; +} + +const char *indtext_by_index_split_default(INDTEXT_DATA * data_list, + int index, + int split_index, + const char *before_split_default_name, const char *default_name) +{ + if (index < split_index) + return indtext_by_index_default(data_list, index, + before_split_default_name); + else + return indtext_by_index_default(data_list, index, default_name); +}; + + +const char *indtext_by_index(INDTEXT_DATA * data_list, unsigned index) +{ + return indtext_by_index_default(data_list, index, NULL); +} + +unsigned indtext_count(INDTEXT_DATA * data_list) +{ + unsigned count = 0; /* return value */ + + if (data_list) { + while (data_list->pString) { + count++; + data_list++; + } + } + return count; +} + +#ifdef TEST +#include +#include "ctest.h" + +static INDTEXT_DATA data_list[] = { + {1, "Joshua"}, + {2, "Mary"}, + {3, "Anna"}, + {4, "Christopher"}, + {5, "Patricia"}, + {0, NULL} +}; + +void testIndexText(Test * pTest) +{ + unsigned i; /*counter */ + const char *pString; + unsigned index; + bool valid; + unsigned count = 0; + + for (i = 0; i < 10; i++) { + pString = indtext_by_index(data_list, i); + if (pString) { + count++; + valid = indtext_by_string(data_list, pString, &index); + ct_test(pTest, valid == true); + ct_test(pTest, index == i); + ct_test(pTest, index == indtext_by_string_default(data_list, + pString, index)); + } + } + ct_test(pTest, indtext_count(data_list) == count); + ct_test(pTest, indtext_by_string(data_list, "Harry", NULL) == false); + ct_test(pTest, indtext_by_string(data_list, NULL, NULL) == false); + ct_test(pTest, indtext_by_string(NULL, NULL, NULL) == false); + ct_test(pTest, indtext_by_index(data_list, 0) == NULL); + ct_test(pTest, indtext_by_index(data_list, 10) == NULL); + ct_test(pTest, indtext_by_index(NULL, 10) == NULL); + /* case insensitive versions */ + ct_test(pTest, indtext_by_istring(data_list, "JOSHUA", NULL) == true); + ct_test(pTest, indtext_by_istring(data_list, "joshua", NULL) == true); + valid = indtext_by_istring(data_list, "ANNA", &index); + ct_test(pTest, index == indtext_by_istring_default(data_list, "ANNA", + index)); +} +#endif + +#ifdef TEST_INDEX_TEXT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("index text", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testIndexText); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_INDEX_TEXT */ diff --git a/bacnet-stack-0-3-0/indtext.h b/bacnet-stack-0-3-0/indtext.h new file mode 100644 index 00000000..01f61f70 --- /dev/null +++ b/bacnet-stack-0-3-0/indtext.h @@ -0,0 +1,95 @@ +/*####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 INDTEXT_H +#define INDTEXT_H + +#include +#include +#include + +/* index and text pairs */ +typedef struct { + unsigned index; /* index number that matches the text */ + const char *pString; /* text pair - use NULL to end the list */ +} INDTEXT_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* Searches for a matching string and returns the index to the string + in the parameter found_index. + If the string is not found, false is returned + If the string is found, true is returned and the found_index contains + the first index where the string was found. */ + bool indtext_by_string(INDTEXT_DATA * data_list, + const char *search_name, unsigned *found_index); +/* case insensitive version */ + bool indtext_by_istring(INDTEXT_DATA * data_list, + const char *search_name, unsigned *found_index); +/* Searches for a matching string and returns the index to the string + or the default_index if the string is not found. */ + unsigned indtext_by_string_default(INDTEXT_DATA * data_list, + const char *search_name, unsigned default_index); +/* case insensitive version */ + unsigned indtext_by_istring_default(INDTEXT_DATA * data_list, + const char *search_name, unsigned default_index); +/* for a given index, return the matching string, + or NULL if not found */ + const char *indtext_by_index(INDTEXT_DATA * data_list, unsigned index); +/* for a given index, return the matching string, + or default_name if not found */ + const char *indtext_by_index_default(INDTEXT_DATA * data_list, + unsigned index, const char *default_name); +/* for a given index, return the matching string, + or default_name if not found. + if the index is before the split, + the before_split_default_name is used */ + const char *indtext_by_index_split_default(INDTEXT_DATA * data_list, + int index, + int split_index, + const char *before_split_default_name, const char *default_name); + +/* returns the number of elements in the list */ + unsigned indtext_count(INDTEXT_DATA * data_list); + +#ifdef TEST +#include "ctest.h" + void testIndexText(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/indtext.mak b/bacnet-stack-0-3-0/indtext.mak new file mode 100644 index 00000000..5ef4d586 --- /dev/null +++ b/bacnet-stack-0-3-0/indtext.mak @@ -0,0 +1,28 @@ +#Makefile to build unit tests +CC = gcc +BASEDIR = . +CFLAGS = -Wall -I. -Itest -g -DTEST -DTEST_INDEX_TEXT + +TARGET = indtext + +SRCS = indtext.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 diff --git a/bacnet-stack-0-3-0/license/gpl-2.txt b/bacnet-stack-0-3-0/license/gpl-2.txt new file mode 100644 index 00000000..3912109b --- /dev/null +++ b/bacnet-stack-0-3-0/license/gpl-2.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/bacnet-stack-0-3-0/license/readme.txt b/bacnet-stack-0-3-0/license/readme.txt new file mode 100644 index 00000000..3179461d --- /dev/null +++ b/bacnet-stack-0-3-0/license/readme.txt @@ -0,0 +1,90 @@ +Licenses for the BACnet Stack at SourceForge + +This BACnet protocol stack implementation is specifically +designed for the embedded BACnet appliance, using a GPL with +exception license (like eCos), which means that any changes +to the core code that are distributed get to come back into +the core code, but the BACnet library can be linked to +proprietary code without it becoming GPL. + +The text of the GPL exception included in each source file +is as follows: + + "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." + +Each file that is subject to the GPL uses the following text comment +at the top of the file: + +/*####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####*/ + +The example and demo files are licensed using an MIT style +license, officially called an Expat License by GNU, and is +compatible with the GPL. The files subject to this license +include the following text comment at the top of the file: + +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + + diff --git a/bacnet-stack-0-3-0/makefile.b32 b/bacnet-stack-0-3-0/makefile.b32 new file mode 100644 index 00000000..5517e3ce --- /dev/null +++ b/bacnet-stack-0-3-0/makefile.b32 @@ -0,0 +1,157 @@ +# Master Makefile for BACnet Stack demos +# for Borland C++ + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +all: readprop writeprop readfile writefile server dcc reinit \ + whois whohas timesync ucov + @echo "demo utilities are in utils directory" + +clean: \ + demo/readprop/makefile.b32 \ + demo/writeprop/makefile.b32 \ + demo/readfile/makefile.b32 \ + demo/writefile/makefile.b32 \ + demo/server/makefile.b32 \ + demo/dcc/makefile.b32 \ + demo/reinit/makefile.b32 \ + demo/whois/makefile.b32 \ + demo/whohas/makefile.b32 \ + demo/ucov/makefile.b32 \ + demo/timesync/makefile.b32 + cd demo/readprop + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/writeprop + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/readfile + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/writefile + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/server + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/dcc + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/reinit + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/whois + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/whohas + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/ucov + make -i -f makefile.b32 clean + cd .. + cd .. + cd demo/timesync + make -i -f makefile.b32 clean + cd .. + cd .. + +readprop: demo/readprop/makefile.b32 + cd demo/readprop + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +writeprop: demo/writeprop/makefile.b32 + cd demo/writeprop + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +readfile: demo/readfile/makefile.b32 + cd demo/readfile + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +writefile: demo/writefile/makefile.b32 + cd demo/writefile + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +server: demo/server/makefile.b32 + cd demo/server + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +dcc: demo/dcc/makefile.b32 + cd demo/dcc + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +reinit: demo/reinit/makefile.b32 + cd demo/reinit + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +whois: demo/whois/makefile.b32 + cd demo/whois + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +whohas: demo/whohas/makefile.b32 + cd demo/whohas + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +ucov: demo/ucov/makefile.b32 + cd demo/ucov + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. + +timesync: demo/timesync/makefile.b32 + cd demo/timesync + make -i -f makefile.b32 clean + make -f makefile.b32 all + make -f makefile.b32 install + cd .. + cd .. diff --git a/bacnet-stack-0-3-0/mstp.ide b/bacnet-stack-0-3-0/mstp.ide new file mode 100644 index 00000000..986ae620 Binary files /dev/null and b/bacnet-stack-0-3-0/mstp.ide differ diff --git a/bacnet-stack-0-3-0/mstp.mak b/bacnet-stack-0-3-0/mstp.mak new file mode 100644 index 00000000..d93124de --- /dev/null +++ b/bacnet-stack-0-3-0/mstp.mak @@ -0,0 +1,29 @@ +#Makefile to build BACnet MS/TP tests +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -g +CFLAGS = -Wall -I. -Itest -DTEST -DTEST_MSTP -g + +OBJS = mstp.o crc.o ringbuf.o test/ctest.o + +TARGET = mstp + +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 + diff --git a/bacnet-stack-0-3-0/npdu.c b/bacnet-stack-0-3-0/npdu.c new file mode 100644 index 00000000..03a37f61 --- /dev/null +++ b/bacnet-stack-0-3-0/npdu.c @@ -0,0 +1,506 @@ +/*####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 +#include +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bits.h" +#include "npdu.h" +#include "apdu.h" + +void npdu_copy_data(BACNET_NPDU_DATA * dest, BACNET_NPDU_DATA * src) +{ + if (dest && src) { + dest->protocol_version = src->protocol_version; + dest->data_expecting_reply = src->data_expecting_reply; + dest->network_layer_message = src->network_layer_message; + dest->priority = src->priority; + dest->network_message_type = src->network_message_type; + dest->vendor_id = src->vendor_id; + dest->hop_count = src->hop_count; + } + + return; +} + +/* + +The following ICI parameters are exchanged with the +various service primitives across an API: + +'destination_address' (DA): the address of the device(s) +intended to receive the service primitive. Its format (device name, +network address, etc.) is a local matter. This address +may also be a multicast, local broadcast or global broadcast type. + +'source_address' (SA): the address of the device from which +the service primitive was received. Its format (device name, +network address, etc.) is a local matter. + +'network_priority' (NP): a four-level network priority parameter +described in 6.2.2. + +'data_expecting_reply' (DER): a Boolean parameter that indicates +whether (TRUE) or not (FALSE) a reply service primitive +is expected for the service being issued. + + +Table 5-1. Applicability of ICI parameters for abstract service primitives + Service Primitive DA SA NP DER +CONF_SERV.request Yes No Yes Yes +CONF_SERV.indication Yes Yes Yes Yes +CONF_SERV.response Yes No Yes Yes +CONF_SERV.confirm Yes Yes Yes No +UNCONF_SERV.request Yes No Yes No +UNCONF_SERV.indication Yes Yes Yes No +REJECT.request Yes No Yes No +REJECT.indication Yes Yes Yes No +SEGMENT_ACK.request Yes No Yes No +SEGMENT_ACK.indication Yes Yes Yes No +ABORT.request Yes No Yes No +ABORT.indication Yes Yes Yes No +*/ + +int npdu_encode_pdu(uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data) +{ + int len = 0; /* return value - number of octets loaded in this function */ + int i = 0; /* counter */ + + + if (npdu && npdu_data) { + /* protocol version */ + npdu[0] = npdu_data->protocol_version; + /* initialize the control octet */ + npdu[1] = 0; + /* Bit 7: 1 indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + /* 0 indicates that the NSDU contains a BACnet APDU. */ + /* Message Type field is absent. */ + if (npdu_data->network_layer_message) + npdu[1] |= BIT7; + /*Bit 6: Reserved. Shall be zero. */ + /*Bit 5: Destination specifier where: */ + /* 0 = DNET, DLEN, DADR, and Hop Count absent */ + /* 1 = DNET, DLEN, and Hop Count present */ + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (dest && dest->net) + npdu[1] |= BIT5; + /* Bit 4: Reserved. Shall be zero. */ + /* Bit 3: Source specifier where: */ + /* 0 = SNET, SLEN, and SADR absent */ + /* 1 = SNET, SLEN, and SADR present */ + /* SLEN = 0 Invalid */ + /* SLEN > 0 specifies length of SADR field */ + if (src && src->net) + npdu[1] |= BIT3; + /* Bit 2: The value of this bit corresponds to the */ + /* data_expecting_reply parameter in the N-UNITDATA primitives. */ + /* 1 indicates that a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + /* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + if (npdu_data->data_expecting_reply) + npdu[1] |= BIT2; + /* Bits 1,0: Network priority where: */ + /* B'11' = Life Safety message */ + /* B'10' = Critical Equipment message */ + /* B'01' = Urgent message */ + /* B'00' = Normal message */ + npdu[1] |= (npdu_data->priority & 0x03); + len = 2; + if (dest && dest->net) { + len += encode_unsigned16(&npdu[len], dest->net); + npdu[len++] = dest->len; + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (dest->len) { + for (i = 0; i < dest->len; i++) { + npdu[len++] = dest->adr[i]; + } + } + } + if (src && src->net) { + len += encode_unsigned16(&npdu[len], src->net); + npdu[len++] = src->len; + /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ + /* SLEN > 0 specifies length of SADR field */ + if (src->len) { + for (i = 0; i < src->len; i++) { + npdu[len++] = src->adr[i]; + } + } + } + /* The Hop Count field shall be present only if the message is */ + /* destined for a remote network, i.e., if DNET is present. */ + /* This is a one-octet field that is initialized to a value of 0xff. */ + if (dest && dest->net) { + npdu[len] = 0xFF; + len++; + } + if (npdu_data->network_layer_message) { + npdu[len] = npdu_data->network_message_type; + len++; + /* Message Type field contains a value in the range 0x80 - 0xFF, */ + /* then a Vendor ID field shall be present */ + if (npdu_data->network_message_type >= 0x80) + len += encode_unsigned16(&npdu[len], npdu_data->vendor_id); + } + } + + return len; +} + +/* Configure the NPDU portion of the packet for an APDU */ +/* This function does not handle the network messages, just APDUs. */ +void npdu_encode_npdu_data(BACNET_NPDU_DATA * npdu_data, + bool data_expecting_reply, BACNET_MESSAGE_PRIORITY priority) +{ + if (npdu_data) { + npdu_data->data_expecting_reply = data_expecting_reply; + npdu_data->protocol_version = BACNET_PROTOCOL_VERSION; + npdu_data->network_layer_message = false; /* false if APDU */ + npdu_data->network_message_type = 0; /* optional */ + npdu_data->vendor_id = 0; /* optional, if net message type is > 0x80 */ + npdu_data->priority = priority; + npdu_data->hop_count = 0; + } +} + +int npdu_decode(uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data) +{ + int len = 0; /* return value - number of octets loaded in this function */ + int i = 0; /* counter */ + uint16_t src_net = 0; + uint16_t dest_net = 0; + uint8_t address_len = 0; + uint8_t mac_octet = 0; + + if (npdu && npdu_data) { + /* Protocol Version */ + npdu_data->protocol_version = npdu[0]; + /* control octet */ + /* Bit 7: 1 indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + /* 0 indicates that the NSDU contains a BACnet APDU. */ + /* Message Type field is absent. */ + npdu_data->network_layer_message = (npdu[1] & BIT7) ? true : false; + /*Bit 6: Reserved. Shall be zero. */ + /* Bit 4: Reserved. Shall be zero. */ + /* Bit 2: The value of this bit corresponds to data expecting reply */ + /* parameter in the N-UNITDATA primitives. */ + /* 1 indicates that a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + /* 0 indicates that other than a BACnet-Confirmed-Request-PDU, */ + /* a segment of a BACnet-ComplexACK-PDU, */ + /* or a network layer message expecting a reply is present. */ + npdu_data->data_expecting_reply = (npdu[1] & BIT2) ? true : false; + /* Bits 1,0: Network priority where: */ + /* B'11' = Life Safety message */ + /* B'10' = Critical Equipment message */ + /* B'01' = Urgent message */ + /* B'00' = Normal message */ + npdu_data->priority = npdu[1] & 0x03; + /* set the offset to where the optional stuff starts */ + len = 2; + /*Bit 5: Destination specifier where: */ + /* 0 = DNET, DLEN, DADR, and Hop Count absent */ + /* 1 = DNET, DLEN, and Hop Count present */ + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + if (npdu[1] & BIT5) { + len += decode_unsigned16(&npdu[len], &dest_net); + /* DLEN = 0 denotes broadcast MAC DADR and DADR field is absent */ + /* DLEN > 0 specifies length of DADR field */ + address_len = npdu[len++]; + if (dest) { + dest->net = dest_net; + dest->len = address_len; + } + if (address_len) { + for (i = 0; i < address_len; i++) { + mac_octet = npdu[len++]; + if (dest) + dest->adr[i] = mac_octet; + } + } + } + /* zero out the destination address */ + else if (dest) { + dest->net = 0; + dest->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + /* Bit 3: Source specifier where: */ + /* 0 = SNET, SLEN, and SADR absent */ + /* 1 = SNET, SLEN, and SADR present */ + /* SLEN = 0 Invalid */ + /* SLEN > 0 specifies length of SADR field */ + if (npdu[1] & BIT3) { + len += decode_unsigned16(&npdu[len], &src_net); + /* SLEN = 0 denotes broadcast MAC SADR and SADR field is absent */ + /* SLEN > 0 specifies length of SADR field */ + address_len = npdu[len++]; + if (src) { + src->net = src_net; + src->len = address_len; + } + if (address_len) { + for (i = 0; i < address_len; i++) { + mac_octet = npdu[len++]; + if (src) + src->adr[i] = mac_octet; + } + } + } else if (src) { + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } + } + /* The Hop Count field shall be present only if the message is */ + /* destined for a remote network, i.e., if DNET is present. */ + /* This is a one-octet field that is initialized to a value of 0xff. */ + if (dest_net) + npdu_data->hop_count = npdu[len++]; + else + npdu_data->hop_count = 0; + /* Indicates that the NSDU conveys a network layer message. */ + /* Message Type field is present. */ + if (npdu_data->network_layer_message) { + npdu_data->network_message_type = npdu[len++]; + /* Message Type field contains a value in the range 0x80 - 0xFF, */ + /* then a Vendor ID field shall be present */ + if (npdu_data->network_message_type >= 0x80) + len += + decode_unsigned16(&npdu[len], &npdu_data->vendor_id); + } else + npdu_data->network_message_type = 0; + } + + return len; +} + +void npdu_handler(BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* length PDU */ + int apdu_offset = 0; + BACNET_ADDRESS dest = { 0 }; + BACNET_NPDU_DATA npdu_data = { 0 }; + + apdu_offset = npdu_decode(&pdu[0], &dest, src, &npdu_data); + if (npdu_data.network_layer_message) { + /*FIXME: network layer message received! Handle it! */ + } else if ((apdu_offset > 0) && (apdu_offset <= pdu_len)) { + /* only handle the version that we know how to handle */ + if (npdu_data.protocol_version == BACNET_PROTOCOL_VERSION) + apdu_handler(src, &pdu[apdu_offset], + (uint16_t) (pdu_len - apdu_offset)); + } + + return; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testNPDU2(Test * pTest) +{ + uint8_t pdu[480] = { 0 }; + BACNET_ADDRESS dest = { 0 }; + BACNET_ADDRESS src = { 0 }; + BACNET_ADDRESS npdu_dest = { 0 }; + BACNET_ADDRESS npdu_src = { 0 }; + int len = 0; + bool data_expecting_reply = true; + BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL; + BACNET_NPDU_DATA npdu_data = { 0 }; + int i = 0; /* counter */ + int npdu_len = 0; + bool network_layer_message = false; /* false if APDU */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */ + uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */ + + /* mac_len = 0 if global address */ + dest.mac_len = 6; + for (i = 0; i < dest.mac_len; i++) { + dest.mac[i] = i; + } + /* DNET,DLEN,DADR */ + dest.net = 1; + dest.len = 6; + for (i = 0; i < dest.len; i++) { + dest.adr[i] = i * 10; + } + src.mac_len = 1; + for (i = 0; i < src.mac_len; i++) { + src.mac[i] = 0x80; + } + /* SNET,SLEN,SADR */ + src.net = 2; + src.len = 1; + for (i = 0; i < src.len; i++) { + src.adr[i] = 0x40; + } + npdu_encode_npdu_data(&npdu_data, true, priority); + len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data); + ct_test(pTest, len != 0); + /* can we get the info back? */ + npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data); + ct_test(pTest, npdu_len != 0); + ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply); + ct_test(pTest, + npdu_data.network_layer_message == network_layer_message); + ct_test(pTest, npdu_data.network_message_type == network_message_type); + ct_test(pTest, npdu_data.vendor_id == vendor_id); + ct_test(pTest, npdu_data.priority == priority); + /* DNET,DLEN,DADR */ + ct_test(pTest, npdu_dest.net == dest.net); + ct_test(pTest, npdu_dest.len == dest.len); + for (i = 0; i < dest.len; i++) { + ct_test(pTest, npdu_dest.adr[i] == dest.adr[i]); + } + /* SNET,SLEN,SADR */ + ct_test(pTest, npdu_src.net == src.net); + ct_test(pTest, npdu_src.len == src.len); + for (i = 0; i < src.len; i++) { + ct_test(pTest, npdu_src.adr[i] == src.adr[i]); + } +} + +void testNPDU1(Test * pTest) +{ + uint8_t pdu[480] = { 0 }; + BACNET_ADDRESS dest = { 0 }; + BACNET_ADDRESS src = { 0 }; + BACNET_ADDRESS npdu_dest = { 0 }; + BACNET_ADDRESS npdu_src = { 0 }; + int len = 0; + bool data_expecting_reply = false; + BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL; + BACNET_NPDU_DATA npdu_data = { 0 }; + int i = 0; /* counter */ + int npdu_len = 0; + bool network_layer_message = false; /* false if APDU */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type = 0; /* optional */ + uint16_t vendor_id = 0; /* optional, if net message type is > 0x80 */ + + /* mac_len = 0 if global address */ + dest.mac_len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest.mac[i] = 0; + } + /* DNET,DLEN,DADR */ + dest.net = 0; + dest.len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest.adr[i] = 0; + } + src.mac_len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src.mac[i] = 0; + } + /* SNET,SLEN,SADR */ + src.net = 0; + src.len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src.adr[i] = 0; + } + npdu_encode_npdu_data(&npdu_data, false, priority); + len = npdu_encode_pdu(&pdu[0], &dest, &src, &npdu_data); + ct_test(pTest, len != 0); + /* can we get the info back? */ + npdu_len = npdu_decode(&pdu[0], &npdu_dest, &npdu_src, &npdu_data); + ct_test(pTest, npdu_len != 0); + ct_test(pTest, npdu_data.data_expecting_reply == data_expecting_reply); + ct_test(pTest, + npdu_data.network_layer_message == network_layer_message); + ct_test(pTest, npdu_data.network_message_type == network_message_type); + ct_test(pTest, npdu_data.vendor_id == vendor_id); + ct_test(pTest, npdu_data.priority == priority); + ct_test(pTest, npdu_dest.mac_len == src.mac_len); + ct_test(pTest, npdu_src.mac_len == dest.mac_len); +} + +#ifdef TEST_NPDU +/* dummy stub for testing */ +void tsm_free_invoke_id(uint8_t invokeID) +{ + (void) invokeID; +} + +void iam_handler(uint8_t * service_request, + uint16_t service_len, BACNET_ADDRESS * src) +{ + (void) service_request; + (void) service_len; + (void) src; +} + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet NPDU", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testNPDU1); + assert(rc); + rc = ct_addTestFunction(pTest, testNPDU2); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_NPDU */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/npdu.h b/bacnet-stack-0-3-0/npdu.h new file mode 100644 index 00000000..2ae54606 --- /dev/null +++ b/bacnet-stack-0-3-0/npdu.h @@ -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 NPDU_H +#define NPDU_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" + +/* an NPDU structure keeps the parameter stack to a minimum */ +typedef struct bacnet_npdu_data_t { + uint8_t protocol_version; + /* parts of the control octet: */ + bool data_expecting_reply; + bool network_layer_message; /* false if APDU */ + BACNET_MESSAGE_PRIORITY priority; + /* optional network message info */ + BACNET_NETWORK_MESSAGE_TYPE network_message_type; /* optional */ + uint16_t vendor_id; /* optional, if net message type is > 0x80 */ + uint8_t hop_count; +} BACNET_NPDU_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + uint8_t npdu_encode_max_seg_max_apdu(int max_segs, int max_apdu); + + int npdu_encode_pdu(uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data); + + void npdu_encode_npdu_data(BACNET_NPDU_DATA * npdu, + bool data_expecting_reply, BACNET_MESSAGE_PRIORITY priority); + + void npdu_copy_data(BACNET_NPDU_DATA * dest, BACNET_NPDU_DATA * src); + + int npdu_decode(uint8_t * npdu, + BACNET_ADDRESS * dest, + BACNET_ADDRESS * src, BACNET_NPDU_DATA * npdu_data); + + void npdu_handler(BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); /* length PDU */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/npdu.mak b/bacnet-stack-0-3-0/npdu.mak new file mode 100644 index 00000000..1cee4f41 --- /dev/null +++ b/bacnet-stack-0-3-0/npdu.mak @@ -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_NPDU -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + npdu.c \ + apdu.c \ + dcc.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = npdu + +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 diff --git a/bacnet-stack-0-3-0/ports/linux/arcnet.c b/bacnet-stack-0-3-0/ports/linux/arcnet.c new file mode 100644 index 00000000..efe6410c --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/arcnet.c @@ -0,0 +1,377 @@ +/*####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 +#include "bacdef.h" +#include "npdu.h" +#include "arcnet.h" +#include "net.h" + +/* my local device data - MAC address */ +uint8_t ARCNET_MAC_Address = 0; +/* ARCNET file handle */ +static int ARCNET_Sock_FD = -1; +/* ARCNET socket address (has the interface name) */ +static struct sockaddr ARCNET_Socket_Address; +/* Broadcast address */ +#define ARCNET_BROADCAST 0 + +/* +Hints: + +When using a PCI20-485D ARCNET card from Contemporary Controls, +you might need to know about the following settings: + +Assuming a 20MHz clock on the COM20020 chip: + +clockp Clock Prescaler DataRate +------ --------------- -------- +0 8 2.5 Mbps +1 16 1.25 Mbps +2 32 625 Kbps +3 64 312.5 Kbps +4 128 156.25Kbps + +1. Install the arcnet driver and arcnet raw mode driver: +# modprobe com20020_pci clockp=4 +# modprobe arc_rawmode + +2. Use ifconfig to bring up the interface +# ifconfig arc0 up + +3. The hardware address (MAC address) is set using the dipswitch + on the back of the card. 0 is broadcast, so don't use 0. + +4. The backplane mode on the PCI20-485D card is done in hardware, + so the driver does not need to do backplane mode. If you + use another type of PCI20 card, you could pass in backplane=1 or + backplane=0 as an option to the modprobe of com20020_pci. + +*/ + +bool arcnet_valid(void) +{ + return (ARCNET_Sock_FD >= 0); +} + +void arcnet_cleanup(void) +{ + if (arcnet_valid()) + close(ARCNET_Sock_FD); + ARCNET_Sock_FD = -1; + + return; +} + +static int arcnet_bind(char *interface_name) +{ + int sock_fd = -1; /* return value */ + struct ifreq ifr; + int rv; /* return value - error value from df or ioctl call */ + int uid = 0; + + /* check to see if we are being run as root */ + uid = getuid(); + if (uid != 0) { + fprintf(stderr, + "arcnet: Unable to open an af_packet socket. " + "Try running with root priveleges.\n"); + return sock_fd; + } + fprintf(stderr, "arcnet: opening \"%s\"\n", interface_name); + /* note: on some systems you may have to add or enable in */ + /* modules.conf (or in modutils/alias on Debian with update-modules) */ + /* alias net-pf-17 af_packet */ + /* Then follow it by: # modprobe af_packet */ + if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) { + /* Error occured */ + fprintf(stderr, + "arcnet: Error opening socket: %s\n", strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" + "Then follow it by:\n" "# modprobe af_packet\n"); + exit(-1); + } + + if (ARCNET_Sock_FD >= 0) { + /* Bind the socket to an interface name so we only get packets from it */ + ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET; + /*ARCNET_Socket_Address.sa_family = PF_INET; */ + /* Clear the memory before copying */ + memset(ARCNET_Socket_Address.sa_data, '\0', + sizeof(ARCNET_Socket_Address.sa_data)); + /* Strcpy the interface name into the address */ + strncpy(ARCNET_Socket_Address.sa_data, interface_name, + sizeof(ARCNET_Socket_Address.sa_data) - 1); + fprintf(stderr, "arcnet: binding \"%s\"\n", + ARCNET_Socket_Address.sa_data); + if (bind(sock_fd, &ARCNET_Socket_Address, + sizeof(ARCNET_Socket_Address)) != 0) { + /* Bind problem, close socket and return */ + fprintf(stderr, + "arcnet: Unable to bind socket : %s\n", strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" + "Then follow it by:\n" "# modprobe af_packet\n"); + /* Close the socket */ + close(sock_fd); + exit(-1); + } + } + strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name)); + rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr); + if (rv != -1) /* worked okay */ + ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0]; + /* copy this info into the local copy since bind wiped it out */ + ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET; + /*ARCNET_Socket_Address.sa_family = PF_INET; */ + /* Clear the memory before copying */ + memset(ARCNET_Socket_Address.sa_data, '\0', + sizeof(ARCNET_Socket_Address.sa_data)); + /* Strcpy the interface name into the address */ + strncpy(ARCNET_Socket_Address.sa_data, interface_name, + sizeof(ARCNET_Socket_Address.sa_data) - 1); + fprintf(stderr, "arcnet: MAC=%02Xh iface=\"%s\"\n", + ARCNET_MAC_Address, ARCNET_Socket_Address.sa_data); + + atexit(arcnet_cleanup); + + return sock_fd; +} + +bool arcnet_init(char *interface_name) +{ + ARCNET_Sock_FD = arcnet_bind(interface_name); + + return arcnet_valid(); +} + +/* function to send a PDU out the socket */ +/* returns number of bytes sent on success, negative 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 */ + BACNET_ADDRESS src = { 0 }; /* source address */ + int bytes = 0; + uint8_t mtu[512] = { 0 }; + int mtu_len = 0; + struct archdr *pkt = (struct archdr *) mtu; + + (void) npdu_data; + src.mac[0] = ARCNET_MAC_Address; + src.mac_len = 1; + + /* don't waste time if the socket is not valid */ + if (ARCNET_Sock_FD < 0) { + fprintf(stderr, "arcnet: socket is invalid!\n"); + return -1; + } + /* load destination MAC address */ + if (dest->mac_len == 1) + pkt->hard.dest = dest->mac[0]; + else { + fprintf(stderr, "arcnet: invalid destination MAC address!\n"); + return -2; + } + if (src.mac_len == 1) + pkt->hard.source = src.mac[0]; + else { + fprintf(stderr, "arcnet: invalid source MAC address!\n"); + return -3; + } + /* Logical PDU portion */ + pkt->soft.raw[0] = 0xCD; /* SC for BACnet */ + pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */ + pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */ + pkt->soft.raw[3] = 0x03; /* LLC Control byte in header */ + /* packet length */ + mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC */ + pdu_len; + if (mtu_len > 512) { + fprintf(stderr, "arcnet: PDU is too big to send!\n"); + return -4; + } + memcpy(&pkt->soft.raw[4], pdu, pdu_len); + /* Send the packet */ + bytes = + sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0, + (struct sockaddr *) &ARCNET_Socket_Address, + sizeof(ARCNET_Socket_Address)); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "arcnet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; +} + +/* 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 */ + int received_bytes; + uint8_t buf[512] = { 0 }; /* data */ + uint16_t pdu_len = 0; /* return value */ + fd_set read_fds; + int max; + struct timeval select_timeout; + struct archdr *pkt = (struct archdr *) buf; + + /* Make sure the socket is open */ + if (ARCNET_Sock_FD <= 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(ARCNET_Sock_FD, &read_fds); + max = ARCNET_Sock_FD; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf)); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, + "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* printf("arcnet: received %u bytes (offset=%02Xh %02Xh) " + "from %02Xh (proto==%02Xh)\n", + received_bytes, pkt->offset[0], pkt->offset[1], + pkt->hard.source, pkt->soft.raw[0]); + */ + + if (pkt->hard.source == ARCNET_MAC_Address) { + fprintf(stderr, "arcnet: self sent packet?\n"); + return 0; + } + if (pkt->soft.raw[0] != 0xCD) { + /* fprintf(stderr,"arcnet: Non-BACnet packet.\n"); */ + return 0; + } + if ((pkt->hard.dest != ARCNET_MAC_Address) && + (pkt->hard.dest != ARCNET_BROADCAST)) { + fprintf(stderr, "arcnet: This packet is not for us.\n"); + return 0; + } + if ((pkt->soft.raw[1] != 0x82) || /* DSAP */ + (pkt->soft.raw[2] != 0x82) || /* LSAP */ + (pkt->soft.raw[3] != 0x03)) { /* LLC Control */ + fprintf(stderr, "arcnet: BACnet packet has invalid LLC.\n"); + return 0; + } + /* It must be addressed to us or be a Broadcast */ + if ((pkt->hard.dest != ARCNET_MAC_Address) && + (pkt->hard.dest != ARCNET_BROADCAST)) { + fprintf(stderr, "arcnet: This packet is not for us.\n"); + return 0; + } + /* copy the source address */ + src->mac_len = 1; + src->mac[0] = pkt->hard.source; + /* compute the PDU length */ + pdu_len = received_bytes - ARC_HDR_SIZE; + pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &pkt->soft.raw[4], pdu_len); + /* silently ignore packets that are too large */ + else + pdu_len = 0; + + return pdu_len; +} + +void arcnet_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 1; + my_address->mac[0] = ARCNET_MAC_Address; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void arcnet_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac[0] = ARCNET_BROADCAST; + dest->mac_len = 1; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/linux/bip-init.c b/bacnet-stack-0-3-0/ports/linux/bip-init.c new file mode 100644 index 00000000..541d3203 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/bip-init.c @@ -0,0 +1,138 @@ +/*####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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "net.h" + + +static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr, + int request) +{ + int fd; + int rv; /* return value */ + + strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name)); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd < 0) + rv = fd; + else + rv = ioctl(fd, request, ifr); + + return rv; +} + +static int get_local_address_ioctl(char *ifname, + struct in_addr *addr, int request) +{ + struct ifreq ifr = { {{0}} }; + struct sockaddr_in *tcpip_address; + int rv; /* return value */ + + rv = get_local_ifr_ioctl(ifname, &ifr, request); + if (rv >= 0) { + tcpip_address = (struct sockaddr_in *) &ifr.ifr_addr; + memcpy(addr, &tcpip_address->sin_addr, sizeof(struct in_addr)); + } + + return rv; +} + +void bip_set_interface(char *ifname) +{ + struct in_addr local_address; + struct in_addr broadcast_address; + + /* setup local address */ + get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR); + bip_set_addr(local_address.s_addr); +#ifdef BIP_DEBUG + fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address)); +#endif + /* setup local broadcast address */ + get_local_address_ioctl(ifname, &broadcast_address, SIOCGIFBRDADDR); + bip_set_broadcast_addr(broadcast_address.s_addr); +#ifdef BIP_DEBUG + fprintf(stderr, "Broadcast Address: %s\n", + inet_ntoa(broadcast_address)); +#endif +} + +bool bip_init(void) +{ + int status = 0; /* return from socket lib calls */ + struct sockaddr_in sin; + int sockopt = 0; + int sock_fd = -1; + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return status; + } + /* allow us to send a broadcast */ + status = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, + &sockopt, sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return status; + } + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(bip_get_port()); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + status = bind(sock_fd, + (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} diff --git a/bacnet-stack-0-3-0/ports/linux/ethernet.c b/bacnet-stack-0-3-0/ports/linux/ethernet.c new file mode 100644 index 00000000..c9fad44d --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/ethernet.c @@ -0,0 +1,404 @@ +/*####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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ + +#include "net.h" +#include "bacdef.h" +#include "ethernet.h" +#include "bacdcode.h" + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 }; + +static int eth802_sockfd = -1; /* 802.2 file handle */ +static struct sockaddr eth_addr = { 0 }; /* used for binding 802.2 */ + +bool ethernet_valid(void) +{ + return (eth802_sockfd >= 0); +} + +void ethernet_cleanup(void) +{ + if (ethernet_valid()) + close(eth802_sockfd); + eth802_sockfd = -1; + + return; +} + +/*---------------------------------------------------------------------- + Portable function to set a socket into nonblocking mode. + Calling this on a socket causes all future read() and write() calls on + that socket to do only as much as they can immediately, and return + without waiting. + If no data can be read or written, they return -1 and set errno + to EAGAIN (or EWOULDBLOCK). + Thanks to Bjorn Reese for this code. +----------------------------------------------------------------------*/ +int setNonblocking(int fd) +{ + int flags; + + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +/* opens an 802.2 socket to receive and send packets */ +static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name) +{ + int sock_fd = -1; /* return value */ + int uid = 0; + + fprintf(stderr, "ethernet: opening \"%s\"\n", interface_name); + /* check to see if we are being run as root */ + uid = getuid(); + if (uid != 0) { + fprintf(stderr, + "ethernet: Unable to open an 802.2 socket. " + "Try running with root priveleges.\n"); + return sock_fd; + } + /* note: on some systems you may have to add or enable in */ + /* modules.conf (or in modutils/alias on Debian with update-modules) */ + /* alias net-pf-17 af_packet */ + /* Then follow it by: # modprobe af_packet */ + + /* Attempt to open the socket for 802.2 ethernet frames */ + if ((sock_fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_802_2))) < 0) { + /* Error occured */ + fprintf(stderr, + "ethernet: Error opening socket: %s\n", strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" + "Then follow it by:\n" "# modprobe af_packet\n"); + exit(-1); + } + /* Bind the socket to an address */ + eth_addr->sa_family = PF_INET; + /* Clear the memory before copying */ + memset(eth_addr->sa_data, '\0', sizeof(eth_addr->sa_data)); + /* Strcpy the interface name into the address */ + strncpy(eth_addr->sa_data, interface_name, + sizeof(eth_addr->sa_data) - 1); + fprintf(stderr, "ethernet: binding \"%s\"\n", eth_addr->sa_data); + /* Attempt to bind the socket to the interface */ + if (bind(sock_fd, eth_addr, sizeof(struct sockaddr)) != 0) { + /* Bind problem, close socket and return */ + fprintf(stderr, + "ethernet: Unable to bind 802.2 socket : %s\n", + strerror(errno)); + fprintf(stderr, + "You might need to add the following to modules.conf\n" + "(or in /etc/modutils/alias on Debian with update-modules):\n" + "alias net-pf-17 af_packet\n" + "Also, add af_packet to /etc/modules.\n" + "Then follow it by:\n" "# modprobe af_packet\n"); + /* Close the socket */ + close(sock_fd); + exit(-1); + } + + atexit(ethernet_cleanup); + + return sock_fd; +} + +/* function to find the local ethernet MAC address */ +static int get_local_hwaddr(const char *ifname, unsigned char *mac) +{ + struct ifreq ifr; + int fd; + int rv; /* return value - error value from df or ioctl call */ + + /* determine the local MAC address */ + strcpy(ifr.ifr_name, ifname); + fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (fd < 0) + rv = fd; + else { + rv = ioctl(fd, SIOCGIFHWADDR, &ifr); + if (rv >= 0) /* worked okay */ + memcpy(mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN); + } + + return rv; +} + +bool ethernet_init(char *interface_name) +{ + get_local_hwaddr(interface_name, Ethernet_MAC_Address); + eth802_sockfd = ethernet_bind(ð_addr, interface_name); + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ +int ethernet_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 */ + int i = 0; /* counter */ + int bytes = 0; + BACNET_ADDRESS src = { 0 }; /* source address for npdu */ + uint8_t mtu[MAX_MPDU] = { 0 }; /* our buffer */ + int mtu_len = 0; + + (void) npdu_data; + /* load the BACnet address for NPDU data */ + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + + /* don't waste time if the socket is not valid */ + if (eth802_sockfd < 0) { + fprintf(stderr, "ethernet: 802.2 socket is invalid!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[i] = dest->mac[i]; + } + } else { + fprintf(stderr, "ethernet: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src.mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[6 + i] = src.mac[i]; + } + } else { + fprintf(stderr, "ethernet: invalid source MAC address!\n"); + return -3; + } + /* Logical PDU portion */ + mtu[14] = 0x82; /* DSAP for BACnet */ + mtu[15] = 0x82; /* SSAP for BACnet */ + mtu[16] = 0x03; /* Control byte in header */ + mtu_len = 17; + if ((mtu_len + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + /* packet length - only the logical portion, not the address */ + encode_unsigned16(&mtu[12], 3 + pdu_len); + + /* Send the packet */ + bytes = + sendto(eth802_sockfd, &mtu, mtu_len, 0, + (struct sockaddr *) ð_addr, sizeof(struct sockaddr)); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "ethernet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_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; + + /* Make sure the socket is open */ + if (eth802_sockfd <= 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(eth802_sockfd, &read_fds); + max = eth802_sockfd; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(eth802_sockfd, &buf[0], sizeof(buf)); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, + "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + /*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { + /*fprintf(stderr, "ethernet: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&buf[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &buf[17], pdu_len); + /* ignore packets that are too large */ + else + pdu_len = 0; + + + return pdu_len; +} + +void ethernet_set_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + + if (info) + fprintf(stderr, "%s", info); + if (dest) { + fprintf(stderr, "Address:\n"); + fprintf(stderr, " MAC Length=%d\n", dest->mac_len); + fprintf(stderr, " MAC Address="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->mac[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " Net=%hu\n", dest->net); + fprintf(stderr, " Len=%d\n", dest->len); + fprintf(stderr, " Adr="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->adr[i]); + } + fprintf(stderr, "\n"); + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/linux/main.c b/bacnet-stack-0-3-0/ports/linux/main.c new file mode 100644 index 00000000..7075ea49 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/main.c @@ -0,0 +1,298 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "net.h" +#include "txbuf.h" + +/* This is an example application using the BACnet Stack on Linux */ +bool Who_Is_Request = true; + +/* buffers used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void LocalIAmHandler(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) src; + (void) service_len; + len = iam_decode_service_request(service_request, + &device_id, &max_apdu, &segmentation, &vendor_id); + fprintf(stderr, "Received I-Am Request"); + if (len != -1) { + fprintf(stderr, " from %u!\n", device_id); + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Read_Properties(void) +{ + uint32_t device_id = 0; + bool status = false; + unsigned max_apdu = 0; + BACNET_ADDRESS src; + bool next_device = false; + static unsigned index = 0; + static unsigned property = 0; + /* list of required (and some optional and proprietary) + properties in the Device Object. Note that this demo + tests for error messages so that the device doesn't have + to have all the properties listed here. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_CONFORMANCE_CLASS, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing because + the result can be very large. Read index 0 + which gives us the number of objects in the list, + and then we can read index 1, 2.. n one by one, + rather than trying to read the entire object + list in one message. */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (address_count()) { + if (address_get_by_index(index, &device_id, &max_apdu, &src)) { + if (object_props[property] < 0) + next_device = true; + else { + /* note: if we wanted to do this synchronously, we would get the + invoke ID from the sending of the request, and wait until we + got the reply with matching invoke ID or the TSM of the + invoke ID expired. This demo is doing things asynchronously. */ + status = Send_Read_Property_Request(device_id, /* destination device */ + OBJECT_DEVICE, + device_id, object_props[property], BACNET_ARRAY_ALL); + if (status) + property++; + } + } else + next_device = true; + if (next_device) { + next_device = false; + index++; + if (index >= MAX_ADDRESS_CACHE) + index = 0; + property = 0; + } + } + + return; +} + +static void Init_Service_Handlers(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, + LocalIAmHandler); + + /* 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); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE, + handler_atomic_read_file_ack); +} + +static void print_address_cache(void) +{ + unsigned i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +static void print_tsm_stats(void) +{ + int idle = 0; + int total = 0; + + idle = tsm_transaction_idle_count(); + total = MAX_TSM_TRANSACTIONS; + fprintf(stderr, "TSM: %d idle of %d transactions\n", idle, total); +} + +static void sig_handler(int signo) +{ + datalink_cleanup(); + print_address_cache(); + print_tsm_stats(); + + exit(0); +} + +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 count = 0; /* milliseconds */ + time_t start_time; + time_t new_time = 0; + + start_time = time(NULL); /* get current time */ + /* Linux specials */ + signal(SIGINT, sig_handler); + signal(SIGHUP, sig_handler); + signal(SIGTERM, sig_handler); + /* setup this BACnet Server device */ + Device_Set_Object_Instance_Number(111); + Init_Service_Handlers(); +#ifdef BACDL_ETHERNET + /* init the physical layer */ + if (!ethernet_init("eth0")) + return 1; +#endif +#ifdef BACDL_BIP + bip_set_interface("eth0"); + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; +#endif +#ifdef BACDL_ARCNET + if (!arcnet_init("arc0")) + return 1; +#endif + + /* loop forever */ + for (;;) { + /* input */ + new_time = 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); + } + if (new_time > start_time) { + tsm_timer_milliseconds(new_time - start_time * 1000); + start_time = new_time; + } + if (I_Am_Request) { + I_Am_Request = false; + iam_send(&Handler_Transmit_Buffer[0]); + } else if (Who_Is_Request) { + Who_Is_Request = false; + Send_WhoIs(-1, -1); + } + /* output */ + /* some round robin task switching */ + count++; + switch (count) { + case 1: + /* used for testing, but kind of noisy on the network */ + /*Read_Properties(); */ + break; + case 2: + break; + default: + count = 0; + break; + } + + /* blink LEDs, Turn on or off outputs, etc */ + } + + return 0; +} diff --git a/bacnet-stack-0-3-0/ports/linux/net.h b/bacnet-stack-0-3-0/ports/linux/net.h new file mode 100644 index 00000000..1afeabce --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/net.h @@ -0,0 +1,99 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 NET_H +#define NET_H + +/* common unix sockets headers needed */ +#include /* basic system data types */ +#include /* timeval{} for select() */ +#include /* timespec{} for pselect() */ +#include /* sockaddr_in{} and other Internet defns */ +#include /* inet(3) functions */ +#include /* for nonblocking */ +#include +#include +#include +#include +#include +#include +#include /* for S_xxx file mode constants */ +#include /* for iovec{} and readv/writev */ +#include +#include +#include /* for Unix domain sockets */ + +#ifdef HAVE_SYS_SELECT_H +# include /* for convenience */ +#endif + +#ifdef HAVE_POLL_H +# include /* for convenience */ +#endif + +#ifdef HAVE_STRINGS_H +# include /* for convenience */ +#endif + +/* Three headers are normally needed for socket/file ioctl's: + * , , and . + */ +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif +#ifdef HAVE_SYS_SOCKIO_H +# include +#endif + +#ifdef HAVE_PTHREAD_H +# include +#endif + + +#define ENUMS +#include +#include +#include +#include +#include /* for the glibc version number */ +#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 +#include +#include /* the L2 protocols */ +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +#endif diff --git a/bacnet-stack-0-3-0/ports/linux/readme.txt b/bacnet-stack-0-3-0/ports/linux/readme.txt new file mode 100644 index 00000000..c24be5af --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/readme.txt @@ -0,0 +1,2 @@ +This is a port to Linux for testing. +The unit tests can be run via the test.sh script. \ No newline at end of file diff --git a/bacnet-stack-0-3-0/ports/linux/rs485.c b/bacnet-stack-0-3-0/ports/linux/rs485.c new file mode 100644 index 00000000..c534e81b --- /dev/null +++ b/bacnet-stack-0-3-0/ports/linux/rs485.c @@ -0,0 +1,93 @@ +/*####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####*/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include + +#include "mstp.h" + +/* Transmits a Frame on the wire */ +void RS485_Send_Frame(struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + + /* in order to avoid line contention */ + while (mstp_port->Turn_Around_Waiting) { + /* wait, yield, or whatever */ + } + + /* Disable the receiver, and enable the transmit line driver. */ + + while (nbytes) { + putc(*buffer, stderr); + buffer++; + nbytes--; + } + + /* Wait until the final stop bit of the most significant CRC octet */ + /* has been transmitted but not more than Tpostdrive. */ + + /* Disable the transmit line driver. */ + + return; +} + +/* called by timer, interrupt(?) or other thread */ +void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port) +{ + if (mstp_port->ReceiveError == true) { + /* wait for state machine to clear this */ + } + /* wait for state machine to read from the DataRegister */ + else if (mstp_port->DataAvailable == false) { + /* check for data */ + + /* if error, */ + /* ReceiveError = TRUE; */ + /* return; */ + + mstp_port->DataRegister = 0; /* FIXME: Get this data from UART or buffer */ + + /* if data is ready, */ + /* DataAvailable = TRUE; */ + /* return; */ + } +} diff --git a/bacnet-stack-0-3-0/ports/pic18/18f252.lkr b/bacnet-stack-0-3-0/ports/pic18/18f252.lkr new file mode 100644 index 00000000..3e0717a3 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/18f252.lkr @@ -0,0 +1,29 @@ +// $Id$ +// File: 18f252.lkr +// Sample linker script for the PIC18F252 processor + +LIBPATH . + +FILES c018i.o +FILES clib.lib +FILES p18f252.lib + +CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED +CODEPAGE NAME=page START=0x2A END=0x7FFF +CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED +CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED +CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED +CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED + +ACCESSBANK NAME=accessram START=0x0 END=0x7F +DATABANK NAME=gpr0 START=0x80 END=0xFF +DATABANK NAME=gpr1 START=0x100 END=0x1FF +DATABANK NAME=gpr2 START=0x200 END=0x2FF +DATABANK NAME=gpr3 START=0x300 END=0x3FF +DATABANK NAME=gpr4 START=0x400 END=0x4FF +DATABANK NAME=gpr5 START=0x500 END=0x5FF +ACCESSBANK NAME=accesssfr START=0xF80 END=0xFFF PROTECTED + +SECTION NAME=CONFIG ROM=config + +STACK SIZE=0x100 RAM=gpr5 diff --git a/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcp b/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcp new file mode 100644 index 00000000..1c4173cc --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcp @@ -0,0 +1,131 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +[PATH_INFO] +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc=C:\code\bacnet-stack;C:\code\bacnet-stack\demo\handler;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18 +dir_lib=C:\mcc18\lib +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm;*.c +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +[FILE_INFO] +file_000=C:\code\bacnet-stack\abort.c +file_001=C:\code\bacnet-stack\apdu.c +file_002=C:\code\bacnet-stack\bacapp.c +file_003=C:\code\bacnet-stack\bacdcode.c +file_004=C:\code\bacnet-stack\bacerror.c +file_005=C:\code\bacnet-stack\bacstr.c +file_006=C:\code\bacnet-stack\crc.c +file_007=C:\code\bacnet-stack\datalink.c +file_008=C:\code\bacnet-stack\dcc.c +file_009=C:\code\bacnet-stack\iam.c +file_010=C:\code\bacnet-stack\mstp.c +file_011=C:\code\bacnet-stack\npdu.c +file_012=C:\code\bacnet-stack\rd.c +file_013=C:\code\bacnet-stack\reject.c +file_014=C:\code\bacnet-stack\rp.c +file_015=C:\code\bacnet-stack\whois.c +file_016=C:\code\bacnet-stack\demo\handler\h_dcc.c +file_017=C:\code\bacnet-stack\demo\handler\h_rd.c +file_018=main.c +file_019=dlmstp.c +file_020=rs485.c +file_021=device.c +file_022=C:\code\bacnet-stack\wp.h +file_023=C:\code\bacnet-stack\abort.h +file_024=C:\code\bacnet-stack\apdu.h +file_025=C:\code\bacnet-stack\bacapp.h +file_026=C:\code\bacnet-stack\bacdcode.h +file_027=C:\code\bacnet-stack\bacdef.h +file_028=C:\code\bacnet-stack\bacenum.h +file_029=C:\code\bacnet-stack\bacerror.h +file_030=C:\code\bacnet-stack\bacstr.h +file_031=C:\code\bacnet-stack\config.h +file_032=C:\code\bacnet-stack\crc.h +file_033=C:\code\bacnet-stack\datalink.h +file_034=C:\code\bacnet-stack\dcc.h +file_035=C:\code\bacnet-stack\dlmstp.h +file_036=C:\code\bacnet-stack\iam.h +file_037=C:\code\bacnet-stack\mstp.h +file_038=C:\code\bacnet-stack\npdu.h +file_039=C:\code\bacnet-stack\rd.h +file_040=C:\code\bacnet-stack\reject.h +file_041=C:\code\bacnet-stack\rp.h +file_042=C:\code\bacnet-stack\rs485.h +file_043=C:\code\bacnet-stack\whois.h +file_044=C:\code\bacnet-stack\demo\handler\client.h +file_045=C:\code\bacnet-stack\demo\handler\handlers.h +file_046=C:\code\bacnet-stack\demo\object\ai.h +file_047=C:\code\bacnet-stack\demo\object\ao.h +file_048=C:\code\bacnet-stack\demo\object\device.h +file_049=stdbool.h +file_050=stdint.h +file_051=hardware.h +[SUITE_INFO] +suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416} +suite_state= +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/o"$(TARGETBASE).cof" /M"$(BINDIR_)$(TARGETBASE).map" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP -DBIG_ENDIAN=1 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa- +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= diff --git a/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcw b/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcw new file mode 100644 index 00000000..eb35cf22 Binary files /dev/null and b/bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcw differ diff --git a/bacnet-stack-0-3-0/ports/pic18/bacnet.mcp b/bacnet-stack-0-3-0/ports/pic18/bacnet.mcp new file mode 100644 index 00000000..ef4678e7 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/bacnet.mcp @@ -0,0 +1,151 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +[PATH_INFO] +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc=c:\mcc18\h;c:\code\bacnet-stack;c:\code\bacnet-stack\ports\pic18;c:\code\bacnet-stack\demo\object;c:\code\bacnet-stack\demo\handler +dir_lib=c:\mcc18\lib +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm;*.c +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +file_052=no +file_053=no +file_054=no +file_055=no +file_056=no +file_057=no +file_058=no +[FILE_INFO] +file_000=rs485.c +file_001=main.c +file_002=init.c +file_003=isr.c +file_004=timer.c +file_005=C:\code\bacnet-stack\rp.c +file_006=C:\code\bacnet-stack\whois.c +file_007=C:\code\bacnet-stack\abort.c +file_008=C:\code\bacnet-stack\apdu.c +file_009=C:\code\bacnet-stack\bacdcode.c +file_010=C:\code\bacnet-stack\bacerror.c +file_011=C:\code\bacnet-stack\crc.c +file_012=C:\code\bacnet-stack\datalink.c +file_013=C:\code\bacnet-stack\iam.c +file_014=C:\code\bacnet-stack\mstp.c +file_015=C:\code\bacnet-stack\npdu.c +file_016=C:\code\bacnet-stack\reject.c +file_017=C:\code\bacnet-stack\dcc.c +file_018=C:\code\bacnet-stack\bacstr.c +file_019=C:\code\bacnet-stack\bacapp.c +file_020=device.c +file_021=dlmstp.c +file_022=C:\code\bacnet-stack\demo\handler\h_rd.c +file_023=C:\code\bacnet-stack\demo\handler\h_dcc.c +file_024=C:\code\bacnet-stack\rd.c +file_025=C:\code\bacnet-stack\demo\handler\txbuf.c +file_026=C:\code\bacnet-stack\demo\handler\h_whois.c +file_027=C:\code\bacnet-stack\demo\handler\noserv.c +file_028=stdbool.h +file_029=stdint.h +file_030=init.h +file_031=timer.h +file_032=C:\code\bacnet-stack\whois.h +file_033=C:\code\bacnet-stack\abort.h +file_034=C:\code\bacnet-stack\apdu.h +file_035=C:\code\bacnet-stack\bacdcode.h +file_036=C:\code\bacnet-stack\bacdef.h +file_037=C:\code\bacnet-stack\bacenum.h +file_038=C:\code\bacnet-stack\bacerror.h +file_039=C:\code\bacnet-stack\bigend.h +file_040=C:\code\bacnet-stack\bits.h +file_041=C:\code\bacnet-stack\bytes.h +file_042=C:\code\bacnet-stack\config.h +file_043=C:\code\bacnet-stack\crc.h +file_044=C:\code\bacnet-stack\datalink.h +file_045=C:\code\bacnet-stack\dlmstp.h +file_046=C:\code\bacnet-stack\mstp.h +file_047=C:\code\bacnet-stack\npdu.h +file_048=C:\code\bacnet-stack\reject.h +file_049=C:\code\bacnet-stack\ringbuf.h +file_050=C:\code\bacnet-stack\rs485.h +file_051=C:\code\bacnet-stack\demo\object\ai.h +file_052=C:\code\bacnet-stack\demo\object\ao.h +file_053=C:\code\bacnet-stack\demo\object\device.h +file_054=C:\code\bacnet-stack\dcc.h +file_055=C:\code\bacnet-stack\bacstr.h +file_056=C:\code\bacnet-stack\bacapp.h +file_057=C:\code\bacnet-stack\rd.h +file_058=18f252.lkr +[SUITE_INFO] +suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416} +suite_state= +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DTSM_ENABLED=0 -DPRINT_ENABLED=0 -DBACDL_MSTP=1 -mL -pa=1 +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}001= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}001=/o"$(TARGETBASE).cof" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}001=-Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa- +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}001= +[ACTIVE_FILE_SETTINGS] +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}001_active=yes diff --git a/bacnet-stack-0-3-0/ports/pic18/bacnet.mcw b/bacnet-stack-0-3-0/ports/pic18/bacnet.mcw new file mode 100644 index 00000000..a7740861 Binary files /dev/null and b/bacnet-stack-0-3-0/ports/pic18/bacnet.mcw differ diff --git a/bacnet-stack-0-3-0/ports/pic18/device.c b/bacnet-stack-0-3-0/ports/pic18/device.c new file mode 100644 index 00000000..d5e68405 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/device.c @@ -0,0 +1,408 @@ +/************************************************************************** +* +* Copyright (C) 2005,2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "device.h" /* me */ + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 0; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; +static uint16_t APDU_Timeout = 3000; +static uint8_t Number_Of_APDU_Retries = 3; + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) + Object_Instance_Number = object_id; + else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +BACNET_DEVICE_STATUS Device_System_Status(void) +{ + return System_Status; +} + +void Device_Set_System_Status(BACNET_DEVICE_STATUS status) +{ + /* FIXME: bounds check? */ + System_Status = status; +} + +/* FIXME: put your vendor ID here! */ +uint16_t Device_Vendor_Identifier(void) +{ + return 0; +} + +uint8_t Device_Protocol_Version(void) +{ + return 1; +} + +uint8_t Device_Protocol_Revision(void) +{ + return 5; +} + +/* FIXME: MAX_APDU is defined in config.ini - set it! */ +uint16_t Device_Max_APDU_Length_Accepted(void) +{ + return MAX_APDU; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported(void) +{ + return SEGMENTATION_NONE; +} + +uint16_t Device_APDU_Timeout(void) +{ + return APDU_Timeout; +} + +/* in milliseconds */ +void Device_Set_APDU_Timeout(uint16_t timeout) +{ + APDU_Timeout = timeout; +} + +uint8_t Device_Number_Of_APDU_Retries(void) +{ + return Number_Of_APDU_Retries; +} + +void Device_Set_Number_Of_APDU_Retries(uint8_t retries) +{ + Number_Of_APDU_Retries = retries; +} + +uint8_t Device_Database_Revision(void) +{ + return 0; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count(void) +{ + unsigned count = 1; + + return count; +} + +bool Device_Object_List_Identifier(unsigned array_index, + int *object_type, uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } + + return status; +} + +/* return the length of the apdu encoded or -1 for error or + -2 for abort */ +int Device_Encode_Property_APDU(uint8_t * apdu, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + characterstring_init_ansi(&char_string, (char *) "TD"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + characterstring_init_ansi(&char_string, (char *) "Tiny"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_tagged_enumerated(&apdu[0], Device_System_Status()); + break; + case PROP_VENDOR_NAME: + characterstring_init_ansi(&char_string, (char *) "ASHRAE"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + characterstring_init_ansi(&char_string, (char *) "GNU"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + characterstring_init_ansi(&char_string, (char *) "1.0"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + characterstring_init_ansi(&char_string, (char *) "1.0"); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision()); + break; + /* BACnet Legacy Support */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + apdu_len = encode_tagged_unsigned(&apdu[0], 1); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_tagged_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + apdu_len = -2; + break; + } + } else { + /* error: internal error? */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + apdu_len = -1; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_tagged_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_tagged_unsigned(&apdu[0], + Device_Max_APDU_Length_Accepted()); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = encode_tagged_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_tagged_unsigned(&apdu[0], APDU_Timeout); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = + encode_tagged_unsigned(&apdu[0], + Device_Number_Of_APDU_Retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Database_Revision()); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testDevice(Test * pTest) +{ + bool status = false; + const char *name = "Patricia"; + + status = Device_Set_Object_Instance_Number(0); + ct_test(pTest, Device_Object_Instance_Number() == 0); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); + ct_test(pTest, Device_Object_Instance_Number() == BACNET_MAX_INSTANCE); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE / 2); + ct_test(pTest, + Device_Object_Instance_Number() == (BACNET_MAX_INSTANCE / 2)); + ct_test(pTest, status == true); + status = Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE + 1); + ct_test(pTest, + Device_Object_Instance_Number() != (BACNET_MAX_INSTANCE + 1)); + ct_test(pTest, status == false); + + return; +} + +#ifdef TEST_DEVICE +/* stubs to dependencies to keep unit test simple */ +char *Analog_Input_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Analog_Input_Count(void) +{ + return 0; +} + +uint32_t Analog_Input_Index_To_Instance(unsigned index) +{ + return index; +} + +char *Analog_Output_Name(uint32_t object_instance) +{ + (void) object_instance; + return ""; +} + +unsigned Analog_Output_Count(void) +{ + return 0; +} + +uint32_t Analog_Output_Index_To_Instance(unsigned index) +{ + return index; +} + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Tiny Device", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_DEVICE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/ports/pic18/dlmstp.c b/bacnet-stack-0-3-0/ports/pic18/dlmstp.c new file mode 100644 index 00000000..9795f4b0 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/dlmstp.c @@ -0,0 +1,328 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" +#include "eeprom.h" + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* receive buffer */ +#pragma udata MSTP_RxData +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +/* local MS/TP port data - shared with RS-485 */ +#pragma udata MSTP_PortData +volatile struct mstp_port_struct_t MSTP_Port; +#pragma udata + +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + +/* This defines the number of edit fields for this module */ +#define MAX_EDIT_FIELD 1 +static uint8_t EditField = 0; +/* ************************************************************************* + DESCRIPTION: This function handles incrementing or decrementing our + EditField + RETURN: none + ALGORITHM: none + NOTES: Pass a #>0 to increment #<0 to decrement + *************************************************************************** */ +void dlmstp_SetEditField(signed char state) +{ /* direction our editfield is moving */ + if (state > 0) { + if (++EditField > MAX_EDIT_FIELD) + EditField = 0; + } else if (state < 0) { + if (EditField) + EditField--; + else + EditField = MAX_EDIT_FIELD; + } else + EditField = 0; +} + +/* ************************************************************************* + DESCRIPTION: Gets the current edit field for this module + RETURN: the current edit field + ALGORITHM: none + NOTES: none + *************************************************************************** */ +uint8_t dlmstp_GetEditField(void) +{ + return (EditField); +} + +void dlmstp_millisecond_timer(void) +{ + INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); +} + +void dlmstp_reinit(void) +{ + RS485_Reinit(); + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); +} + +void dlmstp_init(void) +{ + uint8_t data; + + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Port.InputBuffer = &Receive_Buffer.pdu[0]; + MSTP_Init(&MSTP_Port); + data = I2C_Read_Byte(EEPROM_DEVICE_ADDRESS, EEPROM_MSTP_MAC_ADDR); + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + data = I2C_Read_Byte(EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); + if (data <= 127) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + MSTP_Port.Nmax_info_frames = + I2C_Read_Byte(EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); +} + +void dlmstp_cleanup(void) +{ + /* nothing to do for static buffers */ +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_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 */ + int bytes_sent = 0; + unsigned npdu_len = 0; + uint8_t frame_type = 0; + uint8_t destination = 0; /* destination address */ + BACNET_ADDRESS src; + unsigned i = 0; /* loop counter */ + + if (MSTP_Port.TxReady == false) { + if (npdu_data->data_expecting_reply) + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + else + MSTP_Port.TxFrameType = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + + /* load destination MAC address */ + if (dest && dest->mac_len == 1) { + destination = dest->mac[0]; + } else { + return -2; + } + dlmstp_get_my_address(&src); + if ((8 /* header len */ + pdu_len) > MAX_MPDU) { + return -4; + } + bytes_sent = MSTP_Create_Frame( + (uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), + MSTP_Port.TxFrameType, + destination, MSTP_Port.This_Station, pdu, pdu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + MSTP_Packets++; + } + + return bytes_sent; +} + +void dlmstp_task(void) +{ + uint8_t bytes_remaining; + bool received_frame; + + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + bytes_remaining = RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) + break; + } while (bytes_remaining); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + }; + /*MSTP_Master_Node_FSM(&MSTP_Port); */ + } + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + if (Receive_Buffer.ready && !MSTP_Port.TxReady) { + if (Receive_Buffer.pdu_len) { + MSTP_Packets++; + npdu_handler(&Receive_Buffer.address, + &Receive_Buffer.pdu[0], Receive_Buffer.pdu_len); + } + Receive_Buffer.ready = false; + } + + return; +} + +void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* amount of PDU data */ + /* PDU is already in the Receive_Buffer */ + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + Receive_Buffer.ready = true; +} + +void dlmstp_set_my_address(uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) + MSTP_Port.This_Station = mac_address; + + return; +} + +uint8_t dlmstp_my_address(void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames(unsigned max_info_frames) +{ + MSTP_Port.Nmax_info_frames = max_info_frames; + + return; +} + +unsigned dlmstp_max_info_frames(void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master(uint8_t max_master) +{ + if (max_master <= 127) + MSTP_Port.Nmax_master = max_master; + + return; +} + +uint8_t dlmstp_max_master(void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/pic18/hardware.h b/bacnet-stack-0-3-0/ports/pic18/hardware.h new file mode 100644 index 00000000..fa02d2ca --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/hardware.h @@ -0,0 +1,250 @@ +/************************************************************************** +* +* Copyright (C) 2003 Mark Norton and Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +* Functional +* Description: Defines the hardware implementation for the Microchip +* microprocessor used in the Synergy lighting control project. +* +*********************************************************************/ +#ifndef HARDWARE_H +#define HARDWARE_H + +#include +#include +#include + +/**************************************************************************** + * Card IO * + ****************************************************************************/ +/* + TRIS masks are: + 0 = OUTPUT + 1 = INPUT + + The IO on this card is as follows: + RA0 - SDA - SEEPROM (input) + RA1 - SCL - SEEPROM (input) + RA2 - not used (input) + RA3 - not used (input) + RA4 - LK2a - jumper (input) + RA5 - LK2b - jumper (input) + + TRISA - 0011 1111 - 3Fh + + RB0 - INT - Zero Cross Interrupt (input) + RB1 - LED - I2C Bus Indication (output) + RB2 - LED - Labeled 'DATA' (output) + RB3 - not used (input) + RB4 - CTS input for RS-232 (not used unless LT1180A chip is there) + RB5 - RTS output for RS-232 (not used unless LT1180A chip is there) + RB6 - PGC - in circuit programming (input) + RB7 - PGD - in circuit programming (input) + + TRISB - 1101 1001 - D9h + + RC0 - QH of 74165 shift register (input) + RC1 - SHIFTREG_CKL of 74165 shift register (output) + RC2 - SHIFTREG_LOAD of 74165 shift register (output) + RC3 - SCL for I2C bus (input) + RC4 - SDA for I2C bus (input) + RC5 - RS-485 TXEN (or RS232) (output) + RC6 - RS-485 TXD (or RS232) (output) + RC7 - RS-485 RXD (or RS232) (input) + + TRISC - 1001 1001 - 99h + +*/ + +#define PORT_A_TRIS_MASK 0x3F +#define PORT_B_TRIS_MASK 0xD9 +#define PORT_C_TRIS_MASK 0x99 + +/* hardware mapping of functionality */ +#define DATA_LED_ON() PORTBbits.RB2=0 +#define DATA_LED_OFF() PORTBbits.RB2=1 + +#define ABUS_LED_ON() PORTBbits.RB1=0 +#define ABUS_LED_OFF() PORTBbits.RB1=1 + +#define RS485_TRANSMIT_DISABLE() PORTCbits.RC5=0 +#define RS485_TRANSMIT_ENABLE() PORTCbits.RC5=1 + +/* note: board is inverted logic */ +#define JUMPER_LK2_TOP_OFF() PORTAbits.RA4 +#define JUMPER_LK2_TOP_ON() (!PORTAbits.RA4) +#define JUMPER_LK2_BOTTOM_OFF() PORTAbits.RA5 +#define JUMPER_LK2_BOTTOM_ON() (!PORTAbits.RA5) + +#define ZERO_CROSS PORTBbits.RB0 + +#define I2C_CLK_LATCH LATCbits.LATC3 +#define I2C_DATA_LATCH LATCbits.LATC4 +#define I2C_CLK PORTCbits.RC3 +#define I2C_DATA PORTCbits.RC4 +#define I2C_CLK_HI_Z TRISCbits.TRISC3 +#define I2C_SDA_HI_Z TRISCbits.TRISC4 + +#define EEPROM_DATA_LATCH LATAbits.LATA0 +#define EEPROM_CLK_LATCH LATAbits.LATA1 +#define EEPROM_SDA PORTAbits.RA0 +#define EEPROM_CLK PORTAbits.RA1 +#define EEPROM_SDA_HI_Z TRISAbits.TRISA0 +#define EEPROM_CLK_HI_Z TRISAbits.TRISA1 + +#define SHIFTREG_LOAD PORTCbits.RC2 +#define SHIFTREG_CLK PORTCbits.RC1 +#define SHIFTREG_DATA PORTCbits.RC0 + +#define NO_ANALOGS 0x06 /* None */ +#define ALL_ANALOG 0x00 /* RA0 RA1 RA2 RA3 RA5 RE0 RE1 RE2 Ref=Vdd */ +#define ANALOG_RA3_REF 0x01 /* RA0 RA1 RA2 RA5 RE0 RE1 RE2 Ref=RA3 */ +#define A_ANALOG 0x02 /* RA0 RA1 RA2 RA3 RA5 Ref=Vdd */ +#define A_ANALOG_RA3_REF 0x03 /* RA0 RA1 RA2 RA5 Ref=RA3 */ +#define RA0_RA1_RA3_ANALOG 0x04 /* RA0 RA1 RA3 Ref=Vdd */ +#define RA0_RA1_ANALOG_RA3_REF 0x05 /* RA0 RA1 Ref=RA3 */ + +#define ANALOG_RA3_RA2_REF 0x08 +#define ANALOG_NOT_RE1_RE2 0x09 +#define ANALOG_NOT_RE1_RE2_REF_RA3 0x0A +#define ANALOG_NOT_RE1_RE2_REF_RA3_RA2 0x0B +#define A_ANALOG_RA3_RA2_REF 0x0C +#define RA0_RA1_ANALOG_RA3_RA2_REF 0x0D +#define RA0_ANALOG 0x0E +#define RA0_ANALOG_RA3_RA2_REF 0x0F + +/* Constants used for SETUP_ADC() are: */ +#define ADC_OFF 0 +#define ADC_START 4 +#define ADC_CLOCK_DIV_2 1 +#define ADC_CLOCK_DIV_4 0x101 +#define ADC_CLOCK_DIV_8 0x41 +#define ADC_CLOCK_DIV_16 0x141 +#define ADC_CLOCK_DIV_32 0x81 +#define ADC_CLOCK_DIV_64 0x181 +#define ADC_CLOCK_INTERNAL 0xc1 +#define ADC_DONE_MASK 0x04 + +#define SET_ADC_CHAN(x) ADCON0 = (ADC_CLOCK_DIV_32 | ((x) << 3)) + +#define T1_DISABLED 0 +#define T1_INTERNAL 0x85 +#define T1_EXTERNAL 0x87 +#define T1_EXTERNAL_SYNC 0x83 + +#define T1_CLK_OUT 8 + +#define T1_DIV_BY_1 0 +#define T1_DIV_BY_2 0x10 +#define T1_DIV_BY_4 0x20 +#define T1_DIV_BY_8 0x30 +#define SETUP_TIMER1(mode) T1CON = (mode) + +#define T2_DISABLED 0 +#define T2_DIV_BY_1 4 +#define T2_DIV_BY_4 5 +#define T2_DIV_BY_16 6 +#define SETUP_TIMER2(mode, period, postscale) \ +{ \ + T2CON = ((mode) | ((postscale)-1)<<3); \ + PR2 = (period); \ +} + +#define T3_DISABLED 0 +#define T3_INTERNAL 0x85 +#define T3_EXTERNAL 0x87 +#define T3_EXTERNAL_SYNC 0x83 + +#define T3_DIV_BY_1 0 +#define T3_DIV_BY_2 0x10 +#define T3_DIV_BY_4 0x20 +#define T3_DIV_BY_8 0x30 +#define SETUP_TIMER3(mode) T3CON = (mode) + +#define CCP_OFF 0 +#define CCP_CAPTURE_FE 4 +#define CCP_CAPTURE_RE 5 +#define CCP_CAPTURE_DIV_4 6 +#define CCP_CAPTURE_DIV_16 7 +#define CCP_COMPARE_SET_ON_MATCH 8 +#define CCP_COMPARE_CLR_ON_MATCH 9 +#define CCP_COMPARE_INT 0xA +#define CCP_COMPARE_RESET_TIMER 0xB +#define CCP_PWM 0xC +#define CCP_PWM_PLUS_1 0x1c +#define CCP_PWM_PLUS_2 0x2c +#define CCP_PWM_PLUS_3 0x3c +#define SETUP_CCP1(mode) CCP1CON = (mode) +#define SETUP_CCP2(mode) CCP2CON = (mode) + +#define WATCHDOG_TIMER() \ +{ \ + _asm \ + CLRWDT \ + _endasm \ +} + +#define GLOBAL_INT_ENABLE() INTCONbits.GIE = 1 +#define GLOBAL_INT_DISABLE() INTCONbits.GIE = 0 + +#define PERIPHERAL_INT_ENABLE() INTCONbits.PEIE = 1 +#define PERIPHERAL_INT_DISABLE() INTCONbits.PEIE = 0 + +#define TIMER0_INT_ENABLE() INTCONbits.TMR0IE = 1 +#define TIMER0_INT_DISABLE() INTCONbits.TMR0IE = 0 + +#define TIMER2_INT_ENABLE() PIE1bits.TMR2IE = 1 +#define TIMER2_INT_DISABLE() PIE1bits.TMR2IE = 0 + +#define CCP2_INT_ENABLE() PIE2bits.CCP2IE = 1 +#define CCP2_INT_DISABLE() PIE2bits.CCP2IE = 0 + +#define CCP1_INT_ENABLE() PIE1bits.CCP1IE = 1 +#define CCP1_INT_DISABLE() PIE1bits.CCP1IE = 0 + +#define ABUS_INT_ENABLE() PIE1bits.SSPIE = 1 +#define ABUS_INT_DISABLE() PIE1bits.SSPIE = 0 +#define ABUS_INT_FLAG_CLEAR() PIR1bits.SSPIF = 0 + +#define USART_RX_INT_DISABLE() PIE1bits.RCIE = 0 +#define USART_RX_INT_ENABLE() PIE1bits.RCIE = 1 + +#define USART_TX_INTERRUPT() PIE1bits.TXIE +#define USART_TX_INT_DISABLE() PIE1bits.TXIE = 0 +#define USART_TX_INT_ENABLE() PIE1bits.TXIE = 1 +#define USART_TX_ENABLE() TXSTAbits.TXEN = 1 +#define USART_TX_INT_FLAG_CLEAR() PIR1bits.TXIF = 0 +#define USART_TX_EMPTY() TXSTAbits.TRMT +#define USART_CONTINUOUS_RX_ENABLE() RCSTAbits.CREN = 1 +#define USART_CONTINUOUS_RX_DISABLE() RCSTAbits.CREN = 0 +#define USART_RX_COMPLETE() PIR1bits.RCIF +#define USART_RX_STATUS() RCSTAbits +#define USART_RX_STATUS() RCSTAbits +#define USART_TRANSMIT(x) TXREG = (x) +#define USART_RECEIVE() RCREG +#define USART_RX_FRAME_ERROR() rcstabits.FERR +/* combine the sequence correctly */ +#define USART_RX_SETUP() PIE1bits.RCIE = 1; RCSTAbits.CREN = 1 +#define USART_TX_SETUP() PIE1bits.TXIE = 1; TXSTAbits.TXEN = 1 + + +#endif /* HARDWARE_H */ diff --git a/bacnet-stack-0-3-0/ports/pic18/init.c b/bacnet-stack-0-3-0/ports/pic18/init.c new file mode 100644 index 00000000..08f1494d --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/init.c @@ -0,0 +1,130 @@ +/************************************************************************** +* +* Copyright (C) 2003 Mark Norton +* +* Permission is hereby granted, free of charge, to any 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. +* +* Functional +* Description: Handles the init code for the Microchip microprocessor +* +*********************************************************************/ +#include +#include +#include "hardware.h" + +/* define this to enable ICD */ +/*#define USE_ICD */ + +/* Configuration Bits */ +#pragma config OSC = HS +#pragma config PWRT = ON +#pragma config BOR = ON, BORV = 42 +#pragma config CCP2MUX = ON +#pragma config STVR = ON +#pragma config LVP = OFF +#pragma config CP0 = OFF +#pragma config CP1 = OFF +#pragma config CP2 = OFF +#pragma config CP3 = OFF +#pragma config CPB = OFF +#pragma config CPD = OFF +#pragma config WRT0 = OFF +#pragma config WRT1 = OFF +#pragma config WRT2 = OFF +#pragma config WRT3 = OFF +#pragma config WRTB = OFF +#pragma config WRTC = OFF +#pragma config WRTD = OFF +#pragma config EBTR0 = OFF +#pragma config EBTR1 = OFF +#pragma config EBTR2 = OFF +#pragma config EBTR3 = OFF +#pragma config EBTRB = OFF + +#ifdef USE_ICD +#pragma config WDT = OFF, WDTPS = 128 +#pragma config DEBUG = ON +#else +#pragma config WDT = ON, WDTPS = 128 +#pragma config DEBUG = OFF +#endif /* USE_ICD */ + +#pragma romdata + +/**************************************************************************** +* DESCRIPTION: Initializes the PIC, its timers, WDT, etc. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void init_hardware(void) +{ + /* If the processor gets a power on reset then we can do something. */ + /* We should not get a reset unless there has been */ + /* some kind of power line disturbance. */ + if (RCONbits.POR) { + /*do something special! */ + } + + GLOBAL_INT_DISABLE(); + + /* Setup PORT A */ + TRISA = PORT_A_TRIS_MASK; + + /* PORT A can have analog inputs or digital IO */ + ADCON1 = NO_ANALOGS; + + /* Setup PORT B */ + TRISB = PORT_B_TRIS_MASK; + + /* Setup PORT C */ + TRISC = PORT_C_TRIS_MASK; + + /* setup zero cross interrupt to trigger on a low to high edge */ + INTCON2bits.INTEDG0 = 1; + + /* setup ABUS */ + /*ABUS_LED_OFF(); */ + /*SSPADD = ABUS_DEFAULT_ADDR; */ + /*SSPCON1 = (ABUS_SLAVE_MASK | ABUS_CLK_ENABLE | ABUS_MODE_SETUP); */ + /*ABUS_Clear_SSPBUF(); */ + + /* setup timer2 to reset every 1ms */ + CloseTimer2(); + PR2 = 250; + OpenTimer2(T2_PS_1_4 & T2_POST_1_5 & 0x7F); + + /* Setup our interrupt priorities ---------> all low priority */ + RCONbits.IPEN = 1; + IPR1 = 0; + IPR2 = 0; + INTCON2bits.TMR0IP = 0; + INTCON2bits.RBIP = 0; + INTCON3 = 0; + + /* Enable interrupts */ + TIMER2_INT_ENABLE(); + PERIPHERAL_INT_ENABLE(); + GLOBAL_INT_ENABLE(); + +/* Turn on the Zero cross interrupt */ + INTCONbits.INT0F = 0; + INTCONbits.INT0E = 1; +} diff --git a/bacnet-stack-0-3-0/ports/pic18/init.h b/bacnet-stack-0-3-0/ports/pic18/init.h new file mode 100644 index 00000000..b7d4dabb --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/init.h @@ -0,0 +1,33 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 INIT_H +#define INIT_H + +void init_hardware(void); + + + +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18/isr.c b/bacnet-stack-0-3-0/ports/pic18/isr.c new file mode 100644 index 00000000..b1ef6da6 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/isr.c @@ -0,0 +1,165 @@ +/************************************************************************** +* +* Copyright (C) 2003 Mark Norton and Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +* Functional +* Description: Defines the interrupt service routines (ISR) for the +* Microchip microprocessor used in the Synergy lighting +* control project. +* +*********************************************************************/ +#include +#include "hardware.h" +#include "timer.h" + +/* interrupt service routines */ +extern void RS485_Receive_Interrupt(void); +extern void RS485_Transmit_Interrupt(void); + +/**************************************************************************** +* DESCRIPTION: High priority interrupt routine +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +#pragma interruptlow InterruptHandlerLow save=PROD, section(".tmpdata") +void InterruptHandlerLow(void) +{ + /* check for timer0 interrupt */ + if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) { + /* clear interrupt flag */ + INTCONbits.TMR0IF = 0; + /* call interrupt handler */ + } + /*check for timer1 interrupt */ + if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE)) { + PIR1bits.TMR1IF = 0; + /* call interrupt handler */ + } + /*check for timer2 interrupt */ + if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) { + PIR1bits.TMR2IF = 0; + Timer_Millisecond_Interrupt(); + } + /*check for timer3 interrupt */ + if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) { + PIR2bits.TMR3IF = 0; + /* call interrupt handler */ + } + /*check for compare 1 int */ + if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE)) { + PIR1bits.CCP1IF = 0; + /* call interrupt handler */ + } + /*check for compare 2 int */ + if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) { + PIR2bits.CCP2IF = 0; + /* call interrupt handler */ + } + /*check for USART Rx int */ + if ((PIR2bits.EEIF) && (PIE2bits.EEIE)) { + PIR2bits.EEIF = 0; /*clear interrupt flag */ + EECON1bits.WREN = 0; /* disable writes */ + /* call interrupt handler */ + } + /*check for USART Tx int */ + if ((PIR1bits.TXIF) && (PIE1bits.TXIE)) { + /* call interrupt handler */ + RS485_Transmit_Interrupt(); + } + /*check for USART Rx int */ + if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) { + /* call interrupt handler */ + RS485_Receive_Interrupt(); + } + /*check for AD int */ + if ((PIR1bits.ADIF) && (PIE1bits.ADIE)) { + /* call interrupt handler */ + PIR1bits.ADIF = 0; + } + /*check for I2C receive int (MSSP int) */ + if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE)) { + PIR1bits.SSPIF = 0; + /* call interrupt handler */ + } + + return; +} + +/**************************************************************************** +* DESCRIPTION: High priority interrupt routine +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: don't call functions from this function because registers are +* not saved, and saving registers is slower. +*****************************************************************************/ +#pragma interrupt InterruptHandlerHigh +void InterruptHandlerHigh(void) +{ + /*check for external int */ + if ((INTCONbits.INT0IF) && (INTCONbits.INT0IE)) { + /* Test to ensure that we are not getting a false trigger on the + falling edge. Only trigger on Rising edge. */ + if (ZERO_CROSS) { + /* timer used to determine when power is gone (no zero crosses) */ +/* Power_Timeout = 30; */ +/* if (ABUS_Current_Status.Zerox_Fail) */ +/* { */ +/* ABUS_Flags.SendStatus = TRUE; */ +/* ABUS_Current_Status.Zerox_Fail = FALSE; */ +/* } */ + /* if we get here it means power is good */ +/* System_Flags.PowerFail = FALSE; */ + } + INTCONbits.INT0IF = 0; + } + return; +} + +/**************************************************************************** +* DESCRIPTION: High priority interrupt vector +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: ISRs not here because we would only have 0x10 bytes for code +*****************************************************************************/ +#pragma code InterruptVectorHigh = 0x08 +void InterruptVectorHigh(void) +{ + _asm goto InterruptHandlerHigh /*jump to interrupt routine */ + _endasm} +#pragma code +/**************************************************************************** +* DESCRIPTION: Low priority interrupt vector +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +#pragma code InterruptVectorLow = 0x18 +void InterruptVectorLow(void) +{ + _asm goto InterruptHandlerLow /*jump to interrupt routine */ + _endasm} +#pragma code diff --git a/bacnet-stack-0-3-0/ports/pic18/main.c b/bacnet-stack-0-3-0/ports/pic18/main.c new file mode 100644 index 00000000..d0c53851 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/main.c @@ -0,0 +1,192 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" +#include "init.h" +#include "timer.h" +#include "datalink.h" +#include "handlers.h" +#include "device.h" +#include "hardware.h" +#include "iam.h" +/* for readproperty handler */ +#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" + +/* buffer used for encoding RP apdu */ +static uint8_t Temp_Buf[MAX_APDU]; +/* buffer used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* address where message came from */ +static BACNET_ADDRESS src; +/* address used to send */ +static BACNET_ADDRESS my_address; + +/* see demo/handler/h_rp.c for a more complete example */ +void My_Read_Property_Handler(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; + 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_NPDU_DATA npdu_data; + 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); + /* 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); + } 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); + } else { + 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); + } else + error = true; + } else + error = true; + break; + default: + error = true; + break; + } + } + if (error) { + len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, error_class, error_code); + } + pdu_len += len; + bytes_sent = datalink_send_pdu(src, &npdu_data, + &Handler_Transmit_Buffer[0], pdu_len); + + return; +} + + +/**************************************************************************** +* DESCRIPTION: Handles our calling our module level milisecond counters +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +static void Check_Timer_Milliseconds(void) +{ + /* We might have missed some so keep doing it until we have got them all */ + while (Milliseconds) { + dlmstp_millisecond_timer(); + Milliseconds--; + } +} + +void main(void) +{ + unsigned timeout = 100; /* milliseconds */ + uint16_t pdu_len = 0; + + + /* we need to handle who-is + to support dynamic device binding to us */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + /* 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, + My_Read_Property_Handler); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); + + Device_Set_Object_Instance_Number(5); + dlmstp_set_my_address(0x05); + dlmstp_init(); + + init_hardware(); + + /* broadcast an I-Am on startup */ + iam_send(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + WATCHDOG_TIMER(); + + /* input */ + Check_Timer_Milliseconds(); + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + + /* output */ + + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/pic18/mstp.c b/bacnet-stack-0-3-0/ports/pic18/mstp.c new file mode 100644 index 00000000..47a3d6bd --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/mstp.c @@ -0,0 +1,1706 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" + +/* debug print statements */ +#if PRINT_ENABLED +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 1 +#define PRINT_ENABLED_MASTER 0 +#else +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 0 +#define PRINT_ENABLED_MASTER 0 +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +#define Treply_delay 10 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 255 + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#define Troff 30 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 20 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +#if PRINT_ENABLED_RECEIVE +char *mstp_receive_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_RECEIVE_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + text = "PREAMBLE"; + break; + case MSTP_RECEIVE_STATE_HEADER: + text = "HEADER"; + break; + case MSTP_RECEIVE_STATE_HEADER_CRC: + text = "HEADER_CRC"; + break; + case MSTP_RECEIVE_STATE_DATA: + text = "DATA"; + break; + default: + break; + } + + return text; +} +#endif + +void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstp_receive_state_text(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, + mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + /* MSTP_RECEIVE_STATE_HEADER_CRC */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER_CRC state, the node validates the CRC on the fixed */ + /* message header. */ + case MSTP_RECEIVE_STATE_HEADER_CRC: + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +#if PRINT_ENABLED +char *mstp_master_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_MASTER_STATE_INITIALIZE: + text = "INITIALIZE"; + break; + case MSTP_MASTER_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_USE_TOKEN: + text = "USE_TOKEN"; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + text = "WAIT_FOR_REPLY"; + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + text = "DONE_WITH_TOKEN"; + break; + case MSTP_MASTER_STATE_NO_TOKEN: + text = "NO_TOKEN"; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + text = "ANSWER_DATA_REQUEST"; + break; + default: + break; + } + + return text; +} +#endif + +#if PRINT_ENABLED +char *mstp_frame_type_text(int type) +{ + char *text = "unknown"; + + switch (type) { + case FRAME_TYPE_TOKEN: + text = "TOKEN"; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + text = "REPLY_TO_POLL_FOR_MASTER"; + break; + case FRAME_TYPE_TEST_REQUEST: + text = "TEST_REQUEST"; + break; + case FRAME_TYPE_TEST_RESPONSE: + text = "TEST_RESPONSE"; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + text = "BACNET_DATA_EXPECTING_REPLY"; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + text = "BACNET_DATA_NOT_EXPECTING_REPLY"; + break; + case FRAME_TYPE_REPLY_POSTPONED: + text = "REPLY_POSTPONED"; + break; + default: + if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && + (type <= FRAME_TYPE_PROPRIETARY_MAX)) + text = "PROPRIETARY"; + break; + } + + return text; +} +#endif + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = (mstp_port->Poll_Station + 1) % + (mstp_port->Nmax_master + 1); + next_this_station = (mstp_port->This_Station + 1) % + (mstp_port->Nmax_master + 1); + next_next_station = (mstp_port->Next_Station + 1) % + (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, + next_this_station, + mstp_port->Next_Station, + next_next_station, + mstp_port->Poll_Station, + next_poll_station, + mstp_port->EventCount, + mstp_port->TokenCount, + mstp_port->SilenceTimer, + mstp_master_state_text(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, + mstp_port->DestinationAddress, + mstp_port->DataLength, + mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstp_frame_type_text(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) || + (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /*mstp_port->ReplyPostponedTimer = 0; */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->SilenceTimer > Tusage_delay) { + /* if we missed our timing deadline, another token will be sent */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } else { + /* don't send it if we are too late in getting out */ + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* FIXME: if we knew the APDU type received, we could + see if the next message was that same APDU type */ + if ((mstp_port->SilenceTimer <= Treply_delay) && + mstp_port->TxReady) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + if ((mstp_port->FrameType == + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) + && (mstp_port->TxReady)) { + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], + mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + } + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + else { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* note: This_Station should be set with the MAC address */ +/* note: Nmax_info_frames should be set */ +/* note: Nmax_master should be set */ +void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; +/* mstp_port->ReplyPostponedTimer = 0; */ + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; +#if 0 + /* these are adjustable, so should already be set */ + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; +#endif + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* test stub functions */ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + (void) mstp_port; + (void) buffer; + (void) nbytes; +} + +#define RING_BUFFER_DATA_SIZE 1 +#define RING_BUFFER_SIZE MAX_MPDU +static RING_BUFFER Test_Buffer; +static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE]; +static void Load_Input_Buffer(uint8_t * buffer, size_t len) +{ + static bool initialized = false; /* tracks our init */ + + + if (!initialized) { + initialized = true; + Ringbuf_Init(&Test_Buffer, + (char *) Test_Buffer_Data, + RING_BUFFER_DATA_SIZE, RING_BUFFER_SIZE); + } + /* empty any the existing data */ + while (!Ringbuf_Empty(&Test_Buffer)) { + (void) Ringbuf_Pop_Front(&Test_Buffer); + } + + if (buffer) { + while (len) { + (void) Ringbuf_Put(&Test_Buffer, (char *) buffer); + len--; + buffer++; + } + } +} + +void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + char *data; + if (!Ringbuf_Empty(&Test_Buffer) && mstp_port && + (mstp_port->DataAvailable == false)) { + data = Ringbuf_Pop_Front(&Test_Buffer); + if (data) { + mstp_port->DataRegister = *data; + mstp_port->DataAvailable = true; + } + } +} + +void testReceiveNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { 0 }; + uint8_t data[MAX_MPDU - 8 /*header */ - 2 /*CRC*/] = { 0 }; + + MSTP_Init(&mstp_port, my_mac); + + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + mstp_port.SilenceTimer = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 1); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, FrameType == FRAME_TYPE_TOKEN); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 2); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DestinationAddress == 0x10); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 3); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.SourceAddress == my_mac); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 4); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + ct_test(pTest, HeaderCRC == 0x73); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + ct_test(pTest, mstp_port.HeaderCRC == 0x55); + /* NotForUs */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data, 0, sizeof(data)); + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, my_mac, /* destination */ + my_mac, /* source */ + data, /* data */ + sizeof(data)); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + ct_test(pTest, mstp_port.DataLength == sizeof(data)); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + return; +} + +void testMasterNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + + MSTP_Init(&mstp_port, my_mac); + ct_test(pTest, mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE); + +} + +#endif + +#ifdef TEST_MSTP +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("mstp", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testReceiveNodeFSM); + assert(rc); + rc = ct_addTestFunction(pTest, testMasterNodeFSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18/mstp.h b/bacnet-stack-0-3-0/ports/pic18/mstp.h new file mode 100644 index 00000000..7ff3e79e --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/mstp.h @@ -0,0 +1,246 @@ +/*####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 MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_HEADER_CRC = 3, + MSTP_RECEIVE_STATE_DATA = 4 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t *InputBuffer; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +/* uint16_t ReplyPostponedTimer; */ + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 +#define DEFAULT_MAC_ADDRESS 127 + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround 40 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port); + void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18/readme.txt b/bacnet-stack-0-3-0/ports/pic18/readme.txt new file mode 100644 index 00000000..184eea76 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/readme.txt @@ -0,0 +1,30 @@ +BACnet Stack - SourceForge.net +Build for MPLAB IDE + +These are some settings that are important when building +the BACnet Stack using MPLAB IDE and MCC18 Compiler, + +1. Add the files to the project that you need: +abort.c, apdu.c, bacapp.c, bacdcode.c, bacerror.c, +bacstr.c, bigend.c, crc.c, datalink.c, dcc.c, dlmstp.c, +ima.c, mstp.c, npdu.c, rd.c, reject.c, reject.c, +ringbuf.c, rp.c, whois.c, wp.c + +From demo/object/: device.c or dev_tiny.c, ai.c, ao.c, etc. + +From demo/handler/: h_dcc.c, h_rd.c, h_rp.c, h_wp.c + +2. Project->Options->Project + +General Tab: Include Path: +C:\code\bacnet-stack\;C:\code\bacnet-stack\demo\handler\;C:\code\bacnet-stack\demo\object\;C:\code\bacnet-stack\ports\pic18\ + +MPLAB C18 Tab: Memory Model: +Code: Large Code Model +Data: Large Data Model +Stack: Multi-bank Model + +MPLAB C18 Tab: General: Macro Definitions: +PRINT_ENABLED=0 +BACDL_MSTP=1 +BIG_ENDIAN=0 diff --git a/bacnet-stack-0-3-0/ports/pic18/rs485.c b/bacnet-stack-0-3-0/ports/pic18/rs485.c new file mode 100644 index 00000000..8d20a917 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/rs485.c @@ -0,0 +1,365 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include "hardware.h" +#include "mstp.h" +#include "comm.h" +#include "eeprom.h" + +/* public port info */ +extern volatile struct mstp_port_struct_t MSTP_Port; + +/* the baud rate is adjustable */ +uint32_t RS485_Baud_Rate = 9600; + +/* the ISR and other use this for status and control */ +COMSTAT RS485_Comstat; + +/*#pragma udata MSTPPortData */ +/* the buffer for receiving characters */ +volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; + +/* UART transmission buffer and index */ +volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; + +/**************************************************************************** +* DESCRIPTION: Transmits a frame using the UART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + uint16_t i = 0; /* loop counter */ + uint8_t turnaround_time; + + if (!buffer) + return; + + /* bounds check */ + if (nbytes >= sizeof(RS485_Tx_Buffer)) + return; + + /* buffer is full. Wait for ISR to transmit. */ + while (RS485_Comstat.Tx_Bytes) { + }; + + /* wait 40 bit times since reception */ + if (RS485_Baud_Rate == 9600) + turnaround_time = 4; + else if (RS485_Baud_Rate == 19200) + turnaround_time = 2; + else + turnaround_time = 1; + + while (mstp_port->SilenceTimer < turnaround_time) { + }; + + RS485_Comstat.TxHead = 0; + memcpy((void *) &RS485_Tx_Buffer[0], (void *) buffer, nbytes); + + /*for (i = 0; i < nbytes; i++) { */ + /* /* put the data into the buffer */ */ + /* RS485_Tx_Buffer[i] = *buffer; */ + /* buffer++; */ + /*} */ + RS485_Comstat.Tx_Bytes = nbytes; + /* disable the receiver */ + PIE3bits.RC2IE = 0; + RCSTA2bits.CREN = 0; + /* enable the transceiver */ + RS485_TX_ENABLE = 1; + RS485_RX_DISABLE = 1; + /* enable the transmitter */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 1; + /* per MSTP spec, sort of */ + mstp_port->SilenceTimer = 0; + + return; +} + +/**************************************************************************** +* DESCRIPTION: Checks for data on the receive UART, and handles errors +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t * + mstp_port) +{ + /* check for data */ + if (RS485_Comstat.Rx_Bytes) { + mstp_port->DataRegister = RS485_Rx_Buffer[RS485_Comstat.RxTail]; + if (RS485_Comstat.RxTail >= (sizeof(RS485_Rx_Buffer) - 1)) + RS485_Comstat.RxTail = 0; + else + RS485_Comstat.RxTail++; + RS485_Comstat.Rx_Bytes--; + /* errors? let the state machine know */ + if (RS485_Comstat.Rx_Bufferoverrun) { + RS485_Comstat.Rx_Bufferoverrun = FALSE; + mstp_port->ReceiveError = TRUE; + } + /* We read a good byte */ + else + mstp_port->DataAvailable = TRUE; + } + + return RS485_Comstat.Rx_Bytes; +} + +/* ************************************************************************* + DESCRIPTION: Receives RS485 data stream + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Rx(void) +{ + char dummy; + + if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) { + /* Clear the error */ + RCSTA2bits.CREN = 0; + RCSTA2bits.CREN = 1; + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + } else if (RS485_Comstat.Rx_Bytes < sizeof(RS485_Rx_Buffer)) { + RS485_Rx_Buffer[RS485_Comstat.RxHead] = RCREG2; + if (RS485_Comstat.RxHead >= (sizeof(RS485_Rx_Buffer) - 1)) + RS485_Comstat.RxHead = 0; + else + RS485_Comstat.RxHead++; + RS485_Comstat.Rx_Bytes++; + } else { + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + (void) dummy; + } +} + +/* ************************************************************************* + DESCRIPTION: Transmits a byte using the UART out the RS485 port + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Tx(void) +{ + if (RS485_Comstat.Tx_Bytes) { + /* Get the data byte */ + TXREG2 = RS485_Tx_Buffer[RS485_Comstat.TxHead]; + /* point to the next byte */ + RS485_Comstat.TxHead++; + /* reduce the buffer size */ + RS485_Comstat.Tx_Bytes--; + } else { + /* wait for the USART to be empty */ + while (!TXSTA2bits.TRMT); + /* disable this interrupt */ + PIE3bits.TX2IE = 0; + /* enable the receiver */ + RS485_TX_ENABLE = 0; + RS485_RX_DISABLE = 0; + /* FIXME: might not be necessary */ + PIE3bits.RC2IE = 1; + RCSTA2bits.CREN = 1; + } +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate(void) +{ + return RS485_Baud_Rate; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Set_Baud_Rate(uint32_t baud) +{ + if (baud < 19200) + RS485_Baud_Rate = 9600; + else if (baud < 38400) + RS485_Baud_Rate = 19200; + else if (baud < 57600) + RS485_Baud_Rate = 38400; + else if (baud < 57600) + RS485_Baud_Rate = 57600; + else if (baud < 115200) + RS485_Baud_Rate = 76800; + else + RS485_Baud_Rate = 115200; + + I2C_Write_Block(EEPROM_DEVICE_ADDRESS, + (char *) &RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), EEPROM_MSTP_BAUD_RATE_ADDR); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize_Port(void) +{ + + /* Reset USART registers to POR state */ + TXSTA2 = 0; + RCSTA2 = 0; + /* configure USART for receiving */ + /* since the TX will handle setting up for transmit */ + RCSTA2bits.CREN = 1; + /* Interrupt on receipt */ + PIE3bits.RC2IE = 1; + /* enable the transmitter, disable its interrupt */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 0; + /* setup USART Baud Rate Generator */ + /* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */ + /* Fosc=20MHz + BRGH=1 BRGH=0 + Rate SPBRG Rate SPBRG + ------- ----- ------- ----- + 9615 129 9469 32 + 19230 64 19530 15 + 37878 32 78130 3 + 56818 21 104200 2 + 113630 10 312500 0 + 250000 4 + 625000 1 + 1250000 0 + */ + switch (RS485_Baud_Rate) { + case 19200: + SPBRG2 = 64; + TXSTA2bits.BRGH = 1; + break; + case 38400: + SPBRG2 = 32; + TXSTA2bits.BRGH = 1; + break; + case 57600: + SPBRG2 = 21; + TXSTA2bits.BRGH = 1; + break; + case 76800: + SPBRG2 = 3; + TXSTA2bits.BRGH = 0; + break; + case 115200: + SPBRG2 = 10; + TXSTA2bits.BRGH = 1; + break; + case 9600: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + break; + default: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + RS485_Set_Baud_Rate(9600); + break; + } + /* select async mode */ + TXSTA2bits.SYNC = 0; + /* enable transmitter */ + TXSTA2bits.TXEN = 1; + /* serial port enable */ + RCSTA2bits.SPEN = 1; + /* since we are using RS485, + we need to explicitly say + transmit enable or not */ + RS485_RX_DISABLE = 0; + RS485_TX_ENABLE = 0; +} + +/**************************************************************************** +* DESCRIPTION: Disables the RS485 hardware +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Disable_Port(void) +{ + RCSTA2 &= 0x4F; /* Disable the receiver */ + TXSTA2bits.TXEN = 0; /* and transmitter */ + PIE3 &= 0xCF; /* Disable both interrupts */ +} + +void RS485_Reinit(void) +{ + RS485_Set_Baud_Rate(9600); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the data and the port +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize(void) +{ + /* Init the Rs485 buffers */ + RS485_Comstat.RxHead = 0; + RS485_Comstat.RxTail = 0; + RS485_Comstat.Rx_Bytes = 0; + RS485_Comstat.Rx_Bufferoverrun = FALSE; + RS485_Comstat.TxHead = 0; + RS485_Comstat.TxTail = 0; + RS485_Comstat.Tx_Bytes = 0; + + I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *) &RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), EEPROM_MSTP_BAUD_RATE_ADDR); + + RS485_Initialize_Port(); +} diff --git a/bacnet-stack-0-3-0/ports/pic18/rs485.h b/bacnet-stack-0-3-0/ports/pic18/rs485.h new file mode 100644 index 00000000..4d6f34d2 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/rs485.h @@ -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 RS485_H +#define RS485_H + +#include +#include "mstp.h" +#include "comm.h" + +extern COMSTAT RS485_Comstat; +extern volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; +extern volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; +extern uint32_t RS485_Baud_Rate; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Reinit(void); + void RS485_Initialize(void); + + void RS485_Disable(void); + + void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + + void RS485_Interrupt_Rx(void); + + void RS485_Interrupt_Tx(void); + + uint32_t RS485_Get_Baud_Rate(void); + void RS485_Set_Baud_Rate(uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18/stdbool.h b/bacnet-stack-0-3-0/ports/pic18/stdbool.h new file mode 100644 index 00000000..696ffd85 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef char _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18/stdint.h b/bacnet-stack-0-3-0/ports/pic18/stdint.h new file mode 100644 index 00000000..e9629b20 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/stdint.h @@ -0,0 +1,18 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/bacnet-stack-0-3-0/ports/pic18/timer.c b/bacnet-stack-0-3-0/ports/pic18/timer.c new file mode 100644 index 00000000..7cde9d87 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/timer.c @@ -0,0 +1,45 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 + +volatile uint8_t Milliseconds; /* used for timing stuff - counts up to 0xFF. */ + +/**************************************************************************** +* DESCRIPTION: Timer is set to go off every 1ms. We increment the counter, +* and the main task will decrement the counter. +* PARAMETERS: none +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void Timer_Millisecond_Interrupt(void) +{ + /* Global Milisecond timer */ + if (Milliseconds < 0xFF) + Milliseconds++; + + return; +} diff --git a/bacnet-stack-0-3-0/ports/pic18/timer.h b/bacnet-stack-0-3-0/ports/pic18/timer.h new file mode 100644 index 00000000..44652c14 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18/timer.h @@ -0,0 +1,34 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 TIMER_H +#define TIMER_H + +/* used for timing stuff - counts up to 0xFF. */ +extern volatile uint8_t Milliseconds; + +void Timer_Millisecond_Interrupt(void); + +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/18F6720.lkr b/bacnet-stack-0-3-0/ports/pic18f6720/18F6720.lkr new file mode 100644 index 00000000..cba46077 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/18F6720.lkr @@ -0,0 +1,41 @@ +// $Id: 18f6720.lkr,v 1.1 2003/12/16 14:53:08 GrosbaJ Exp $ +// File: 18f6720.lkr +// Sample linker script for the PIC18F6720 processor + +LIBPATH . + +FILES c018i.o +FILES clib.lib +FILES p18f6720.lib + +CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED +CODEPAGE NAME=page START=0x2A END=0x1FFFF +CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED +CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED +CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED +CODEPAGE NAME=eedata START=0xF00000 END=0xF003FF PROTECTED + +ACCESSBANK NAME=accessram START=0x0 END=0x5F +DATABANK NAME=gpr0 START=0x60 END=0xFF +DATABANK NAME=gpr1 START=0x100 END=0x1FF +DATABANK NAME=gpr2 START=0x200 END=0x2FF +DATABANK NAME=gpr3 START=0x300 END=0x3FF +DATABANK NAME=gpr4 START=0x400 END=0x4FF +DATABANK NAME=gpr5 START=0x500 END=0x5FF +DATABANK NAME=gpr6 START=0x600 END=0x6FF +DATABANK NAME=gpr7 START=0x700 END=0x7FF +DATABANK NAME=gpr8 START=0x800 END=0x8FF +DATABANK NAME=gpr9 START=0x900 END=0x9FF +DATABANK NAME=gpr10 START=0xA00 END=0xAFF +DATABANK NAME=gpr11 START=0xB00 END=0xBFF +//DATABANK NAME=gpr12 START=0xC00 END=0xCFF +//DATABANK NAME=gpr13 START=0xD00 END=0xDFF +DATABANK NAME=stackreg START=0xC00 END=0xDFF PROTECTED +DATABANK NAME=gpr14 START=0xE00 END=0xEF3 +DATABANK NAME=dbgspr START=0xEF4 END=0xEFF PROTECTED +ACCESSBANK NAME=accesssfr START=0xF60 END=0xFFF PROTECTED + +SECTION NAME=CONFIG ROM=config + +//STACK SIZE=0x100 RAM=gpr13 +STACK SIZE=0x200 RAM=stackreg diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcp b/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcp new file mode 100644 index 00000000..255a4791 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcp @@ -0,0 +1,147 @@ +[HEADER] +magic_cookie={66E99B07-E706-4689-9E80-9B2582898A13} +file_version=1.0 +[PATH_INFO] +dir_src= +dir_bin= +dir_tmp= +dir_sin= +dir_inc=C:\code\bacnet-stack;C:\code\bacnet-stack\demo\handler;C:\code\bacnet-stack\demo\object;C:\code\bacnet-stack\ports\pic18 +dir_lib=C:\mcc18\lib +dir_lkr= +[CAT_FILTERS] +filter_src=*.asm;*.c +filter_inc=*.h;*.inc +filter_obj=*.o +filter_lib=*.lib +filter_lkr=*.lkr +[OTHER_FILES] +file_000=no +file_001=no +file_002=no +file_003=no +file_004=no +file_005=no +file_006=no +file_007=no +file_008=no +file_009=no +file_010=no +file_011=no +file_012=no +file_013=no +file_014=no +file_015=no +file_016=no +file_017=no +file_018=no +file_019=no +file_020=no +file_021=no +file_022=no +file_023=no +file_024=no +file_025=no +file_026=no +file_027=no +file_028=no +file_029=no +file_030=no +file_031=no +file_032=no +file_033=no +file_034=no +file_035=no +file_036=no +file_037=no +file_038=no +file_039=no +file_040=no +file_041=no +file_042=no +file_043=no +file_044=no +file_045=no +file_046=no +file_047=no +file_048=no +file_049=no +file_050=no +file_051=no +file_052=no +file_053=no +file_054=no +file_055=no +file_056=no +file_057=no +file_058=no +file_059=no +[FILE_INFO] +file_000=C:\code\bacnet-stack\abort.c +file_001=C:\code\bacnet-stack\apdu.c +file_002=C:\code\bacnet-stack\bacapp.c +file_003=C:\code\bacnet-stack\bacdcode.c +file_004=C:\code\bacnet-stack\bacerror.c +file_005=C:\code\bacnet-stack\bacstr.c +file_006=C:\code\bacnet-stack\crc.c +file_007=C:\code\bacnet-stack\dcc.c +file_008=C:\code\bacnet-stack\iam.c +file_009=C:\code\bacnet-stack\npdu.c +file_010=C:\code\bacnet-stack\rd.c +file_011=C:\code\bacnet-stack\reject.c +file_012=C:\code\bacnet-stack\rp.c +file_013=C:\code\bacnet-stack\whois.c +file_014=C:\code\bacnet-stack\demo\handler\h_dcc.c +file_015=C:\code\bacnet-stack\demo\handler\h_rd.c +file_016=main.c +file_017=dlmstp.c +file_018=device.c +file_019=rs485.c +file_020=isr.c +file_021=C:\code\bacnet-stack\datetime.c +file_022=C:\code\bacnet-stack\demo\handler\txbuf.c +file_023=C:\code\bacnet-stack\demo\handler\h_whois.c +file_024=mstp.c +file_025=C:\code\bacnet-stack\demo\handler\h_rp_tiny.c +file_026=C:\code\bacnet-stack\wp.h +file_027=C:\code\bacnet-stack\abort.h +file_028=C:\code\bacnet-stack\apdu.h +file_029=C:\code\bacnet-stack\bacapp.h +file_030=C:\code\bacnet-stack\bacdcode.h +file_031=C:\code\bacnet-stack\bacdef.h +file_032=C:\code\bacnet-stack\bacenum.h +file_033=C:\code\bacnet-stack\bacerror.h +file_034=C:\code\bacnet-stack\bacstr.h +file_035=C:\code\bacnet-stack\config.h +file_036=C:\code\bacnet-stack\crc.h +file_037=C:\code\bacnet-stack\dcc.h +file_038=C:\code\bacnet-stack\dlmstp.h +file_039=C:\code\bacnet-stack\iam.h +file_040=C:\code\bacnet-stack\npdu.h +file_041=C:\code\bacnet-stack\rd.h +file_042=C:\code\bacnet-stack\reject.h +file_043=C:\code\bacnet-stack\rp.h +file_044=C:\code\bacnet-stack\whois.h +file_045=C:\code\bacnet-stack\demo\handler\client.h +file_046=C:\code\bacnet-stack\demo\handler\handlers.h +file_047=C:\code\bacnet-stack\demo\object\ai.h +file_048=C:\code\bacnet-stack\demo\object\ao.h +file_049=C:\code\bacnet-stack\demo\object\device.h +file_050=stdbool.h +file_051=stdint.h +file_052=hardware.h +file_053=rs485.h +file_054=C:\code\bacnet-stack\datetime.h +file_055=C:\code\bacnet-stack\demo\handler\txbuf.h +file_056=mstp.h +file_057=C:\code\bacnet-stack\datalink.h +file_058=C:\mcc18\h\p18f6720.h +file_059=18F6720.lkr +[SUITE_INFO] +suite_guid={5B7D72DD-9861-47BD-9F60-2BE967BF8416} +suite_state= +[TOOL_SETTINGS] +TS{DD2213A8-6310-47B1-8376-9430CDFC013F}= +TS{BFD27FBA-4A02-4C0E-A5E5-B812F3E7707C}=/m"$(BINDIR_)$(TARGETBASE).map" /o"$(TARGETBASE).cof" +TS{C2AF05E7-1416-4625-923D-E114DB6E2B96}=-DPRINT_ENABLED=0 -DBACDL_MSTP=1 -DTSM_ENABLED=0 -DBIG_ENDIAN=0 -mL -Ls -Ou- -Ot- -Ob- -Op- -Or- -Od- -Opa- +TS{ADE93A55-C7C7-4D4D-A4BA-59305F7D0391}= diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcw b/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcw new file mode 100644 index 00000000..825a749e Binary files /dev/null and b/bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcw differ diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/device.c b/bacnet-stack-0-3-0/ports/pic18f6720/device.c new file mode 100644 index 00000000..9b12c1bb --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/device.c @@ -0,0 +1,567 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include /* for memmove */ +#include "bacdef.h" +#include "bacdcode.h" +#include "bacstr.h" +#include "bacenum.h" +#include "config.h" /* the custom stuff */ +#include "apdu.h" +#include "device.h" /* me */ +#include "dlmstp.h" +#include "rs485.h" +#include "ai.h" +#include "bi.h" +#include "bv.h" +#include "wp.h" +#include "dcc.h" + +/* note: you really only need to define variables for + properties that are writable or that may change. + The properties that are constant can be hard coded + into the read-property encoding. */ +static uint32_t Object_Instance_Number = 12345; +static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; + +BACNET_REINITIALIZED_STATE_OF_DEVICE Reinitialize_State = + REINITIALIZED_STATE_IDLE; + +void Device_Reinit(void) +{ + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + Device_Set_Object_Instance_Number(BACNET_MAX_INSTANCE); +} + +void Device_Init(void) +{ + Reinitialize_State = REINITIALIZED_STATE_IDLE; + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* FIXME: Get the data from the eeprom */ + /* I2C_Read_Block(EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ +} + +/* methods to manipulate the data */ +uint32_t Device_Object_Instance_Number(void) +{ + return Object_Instance_Number; +} + +bool Device_Set_Object_Instance_Number(uint32_t object_id) +{ + bool status = true; /* return value */ + + if (object_id <= BACNET_MAX_INSTANCE) { + Object_Instance_Number = object_id; + /* FIXME: Write the data to the eeprom */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&Object_Instance_Number, + sizeof(Object_Instance_Number), + EEPROM_BACNET_ID_ADDR); */ + } else + status = false; + + return status; +} + +bool Device_Valid_Object_Instance_Number(uint32_t object_id) +{ + /* BACnet allows for a wildcard instance number */ + return ((Object_Instance_Number == object_id) || + (object_id == BACNET_MAX_INSTANCE)); +} + +BACNET_DEVICE_STATUS Device_System_Status(void) +{ + return System_Status; +} + +void Device_Set_System_Status(BACNET_DEVICE_STATUS status) +{ + if (status < MAX_DEVICE_STATUS) + System_Status = status; +} + +/* FIXME: put your vendor ID here! */ +uint16_t Device_Vendor_Identifier(void) +{ + return 0; +} + +uint8_t Device_Protocol_Version(void) +{ + return 1; +} + +uint8_t Device_Protocol_Revision(void) +{ + return 5; +} + +/* FIXME: MAX_APDU is defined in config.ini - set it! */ +uint16_t Device_Max_APDU_Length_Accepted(void) +{ + return MAX_APDU; +} + +BACNET_SEGMENTATION Device_Segmentation_Supported(void) +{ + return SEGMENTATION_NONE; +} + +uint16_t Device_APDU_Timeout(void) +{ + return 60000; +} + + +uint8_t Device_Number_Of_APDU_Retries(void) +{ + return 0; +} + +uint8_t Device_Database_Revision(void) +{ + return 0; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +unsigned Device_Object_List_Count(void) +{ + unsigned count = 1; /* at least 1 for device object */ + +/* FIXME: add objects as needed */ +#if 0 + count += Binary_Value_Count(); + count += Analog_Input_Count(); + count += Binary_Input_Count(); +#endif + + return count; +} + +/* Since many network clients depend on the object list */ +/* for discovery, it must be consistent! */ +bool Device_Object_List_Identifier(unsigned array_index, + int *object_type, uint32_t * instance) +{ + bool status = false; + unsigned object_index = 0; + unsigned object_count = 0; + + /* device object */ + if (array_index == 1) { + *object_type = OBJECT_DEVICE; + *instance = Object_Instance_Number; + status = true; + } +#if 0 + /* FIXME: add objects as needed */ + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + /* array index starts at 1, and 1 for the device object */ + object_index = array_index - 2; + object_count = Binary_Value_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_VALUE; + *instance = Binary_Value_Index_To_Instance(object_index); + status = true; + } + } + /* analog input objects */ + if (!status) { + /* array index starts at 1, and 1 for the device object */ + object_index -= object_count; + object_count = Analog_Input_Count(); + if (object_index < object_count) { + *object_type = OBJECT_ANALOG_INPUT; + *instance = Analog_Input_Index_To_Instance(object_index); + status = true; + } + } + /* binary input objects */ + if (!status) { + /* normalize the index since + we know it is not the previous objects */ + object_index -= object_count; + object_count = Binary_Input_Count(); + /* is it a valid index for this object? */ + if (object_index < object_count) { + *object_type = OBJECT_BINARY_INPUT; + *instance = Binary_Input_Index_To_Instance(object_index); + status = true; + } + } +#endif + + return status; +} + +/* return the length of the apdu encoded or -1 for error */ +int Device_Encode_Property_APDU(uint8_t * apdu, + BACNET_PROPERTY_ID property, + int32_t array_index, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + int apdu_len = 0; /* return value */ + int len = 0; /* apdu len intermediate value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + unsigned i = 0; + int object_type = 0; + uint32_t instance = 0; + unsigned count = 0; + BACNET_TIME local_time; + BACNET_DATE local_date; + uint8_t year = 0; + char string_buffer[24]; + int16_t TimeZone = 0; + + /* FIXME: change the hardcoded names to suit your application */ + switch (property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_tagged_object_id(&apdu[0], OBJECT_DEVICE, + Object_Instance_Number); + break; + case PROP_OBJECT_NAME: + (void) strcpypgm2ram(&string_buffer[0], "PIC18F6720 Device"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_OBJECT_TYPE: + apdu_len = encode_tagged_enumerated(&apdu[0], OBJECT_DEVICE); + break; + case PROP_DESCRIPTION: + (void) strcpypgm2ram(&string_buffer[0], "BACnet Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_SYSTEM_STATUS: + apdu_len = + encode_tagged_enumerated(&apdu[0], Device_System_Status()); + break; + case PROP_VENDOR_NAME: + (void) strcpypgm2ram(&string_buffer[0], "ASHRAE"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_VENDOR_IDENTIFIER: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Vendor_Identifier()); + break; + case PROP_MODEL_NAME: + (void) strcpypgm2ram(&string_buffer[0], "GNU Demo"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_FIRMWARE_REVISION: + (void) strcpypgm2ram(&string_buffer[0], "1.00"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_APPLICATION_SOFTWARE_VERSION: + (void) strcpypgm2ram(&string_buffer[0], "1.00"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_LOCATION: + (void) strcpypgm2ram(&string_buffer[0], "USA"); + characterstring_init_ansi(&char_string, string_buffer); + apdu_len = encode_tagged_character_string(&apdu[0], &char_string); + break; + case PROP_PROTOCOL_VERSION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Version()); + break; + case PROP_PROTOCOL_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Protocol_Revision()); + break; + /* BACnet Legacy Support */ + case PROP_PROTOCOL_CONFORMANCE_CLASS: + apdu_len = encode_tagged_unsigned(&apdu[0], 1); + break; + case PROP_PROTOCOL_SERVICES_SUPPORTED: + /* Note: list of services that are executed, not initiated. */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_BACNET_SERVICES_SUPPORTED; i++) { + /* automatic lookup based on handlers set */ + bitstring_set_bit(&bit_string, (uint8_t) i, + apdu_service_supported(i)); + } + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED: + /* Note: this is the list of objects that can be in this device, + not a list of objects that this device can access */ + bitstring_init(&bit_string); + for (i = 0; i < MAX_ASHRAE_OBJECT_TYPE; i++) { + /* initialize all the object types to not-supported */ + bitstring_set_bit(&bit_string, (uint8_t) i, false); + } + /* FIXME: indicate the objects that YOU support */ + bitstring_set_bit(&bit_string, OBJECT_DEVICE, true); +#if 0 + bitstring_set_bit(&bit_string, OBJECT_BINARY_VALUE, true); + bitstring_set_bit(&bit_string, OBJECT_ANALOG_INPUT, true); + bitstring_set_bit(&bit_string, OBJECT_BINARY_INPUT, true); +#endif + apdu_len = encode_tagged_bitstring(&apdu[0], &bit_string); + break; + case PROP_OBJECT_LIST: + count = Device_Object_List_Count(); + /* Array element zero is the number of objects in the list */ + if (array_index == 0) + apdu_len = encode_tagged_unsigned(&apdu[0], count); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. Note that more than likely you will have */ + /* to return an error if the number of encoded objects exceeds */ + /* your maximum APDU size. */ + else if (array_index == BACNET_ARRAY_ALL) { + for (i = 1; i <= count; i++) { + if (Device_Object_List_Identifier(i, &object_type, + &instance)) { + len = + encode_tagged_object_id(&apdu[apdu_len], + object_type, instance); + apdu_len += len; + /* assume next one is the same size as this one */ + /* can we all fit into the APDU? */ + if ((apdu_len + len) >= MAX_APDU) { + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT; + apdu_len = -1; + break; + } + } else { + /* error: internal error? */ + *error_class = ERROR_CLASS_SERVICES; + *error_code = ERROR_CODE_OTHER; + apdu_len = -1; + break; + } + } + } else { + if (Device_Object_List_Identifier(array_index, &object_type, + &instance)) + apdu_len = + encode_tagged_object_id(&apdu[0], object_type, + instance); + else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + case PROP_MAX_APDU_LENGTH_ACCEPTED: + apdu_len = encode_tagged_unsigned(&apdu[0], + Device_Max_APDU_Length_Accepted()); + break; + case PROP_SEGMENTATION_SUPPORTED: + apdu_len = encode_tagged_enumerated(&apdu[0], + Device_Segmentation_Supported()); + break; + case PROP_APDU_TIMEOUT: + apdu_len = encode_tagged_unsigned(&apdu[0], Device_APDU_Timeout()); + break; + case PROP_NUMBER_OF_APDU_RETRIES: + apdu_len = + encode_tagged_unsigned(&apdu[0], + Device_Number_Of_APDU_Retries()); + break; + case PROP_DEVICE_ADDRESS_BINDING: + /* FIXME: encode the list here, if it exists */ + break; + case PROP_DATABASE_REVISION: + apdu_len = + encode_tagged_unsigned(&apdu[0], Device_Database_Revision()); + break; + case PROP_MAX_INFO_FRAMES: + apdu_len = + encode_tagged_unsigned(&apdu[0], dlmstp_max_info_frames()); + break; + case PROP_MAX_MASTER: + apdu_len = encode_tagged_unsigned(&apdu[0], dlmstp_max_master()); + break; + case PROP_LOCAL_TIME: + /* FIXME: if you support time */ + local_time.hour = 0; + local_time.min = 0; + local_time.sec = 0; + local_time.hundredths = 0; + apdu_len = encode_tagged_time(&apdu[0], &local_time); + break; + case PROP_UTC_OFFSET: + /* Note: BACnet Time Zone is inverse of everybody else */ + apdu_len = encode_tagged_signed(&apdu[0], 5 /* EST */ ); + break; + case PROP_LOCAL_DATE: + /* FIXME: if you support date */ + local_date.year = 2006; /* AD */ + local_date.month = 4; /* Jan=1..Dec=12 */ + local_date.day = 11; /* 1..31 */ + local_date.wday = 0; /* 1=Mon..7=Sun */ + apdu_len = encode_tagged_date(&apdu[0], &local_date); + break; + case PROP_DAYLIGHT_SAVINGS_STATUS: + /* FIXME: if you support time/date */ + apdu_len = encode_tagged_boolean(&apdu[0], false); + break; + case 9600: + apdu_len = encode_tagged_unsigned(&apdu[0], RS485_Get_Baud_Rate()); + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + return apdu_len; +} + +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA * wp_data, + BACNET_ERROR_CLASS * error_class, BACNET_ERROR_CODE * error_code) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + + if (!Device_Valid_Object_Instance_Number(wp_data->object_instance)) { + *error_class = ERROR_CLASS_OBJECT; + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + /* decode the some of the request */ + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + /* FIXME: len == 0: unable to decode? */ + switch (wp_data->object_property) { + case PROP_OBJECT_IDENTIFIER: + if (value.tag == BACNET_APPLICATION_TAG_OBJECT_ID) { + if ((value.type.Object_Id.type == OBJECT_DEVICE) && + (Device_Set_Object_Instance_Number(value.type. + Object_Id.instance))) { + /* we could send an I-Am broadcast to let the world know */ + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_INFO_FRAMES: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int <= 255) { + dlmstp_set_max_info_frames(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_MAX_MASTER: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if ((value.type.Unsigned_Int > 0) && + (value.type.Unsigned_Int <= 127)) { + dlmstp_set_max_master(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case PROP_OBJECT_NAME: + if (value.tag == BACNET_APPLICATION_TAG_CHARACTER_STRING) { + uint8_t encoding; + size_t len; + + encoding = + characterstring_encoding(&value.type.Character_String); + len = characterstring_length(&value.type.Character_String); + if (encoding == CHARACTER_ANSI_X34) { + if (len <= 20) { + /* FIXME: set the name */ + /* Display_Set_Name( + characterstring_value(&value.type.Character_String)); */ + + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + case 9600: + if (value.tag == BACNET_APPLICATION_TAG_UNSIGNED_INT) { + if (value.type.Unsigned_Int > 115200) { + RS485_Set_Baud_Rate(value.type.Unsigned_Int); + status = true; + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + break; + default: + *error_class = ERROR_CLASS_PROPERTY; + *error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + } + + return status; +} diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.c b/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.c new file mode 100644 index 00000000..60c09807 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.c @@ -0,0 +1,325 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" + +/* Number of MS/TP Packets Rx/Tx */ +uint16_t MSTP_Packets = 0; + +/* receive buffer */ +#pragma udata MSTP_RxData +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +/* local MS/TP port data - shared with RS-485 */ +#pragma udata MSTP_PortData +volatile struct mstp_port_struct_t MSTP_Port; +#pragma udata + +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + +void dlmstp_millisecond_timer(void) +{ + INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); +} + +void dlmstp_reinit(void) +{ + RS485_Reinit(); + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); + dlmstp_set_max_master(DEFAULT_MAX_MASTER); +} + +void dlmstp_init(void) +{ + uint8_t data; + + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Port.InputBuffer = &Receive_Buffer.pdu[0]; + MSTP_Init(&MSTP_Port); + /* FIXME: implement your data storage */ + data = 64; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAC_ADDR); */ + if (data <= 127) + MSTP_Port.This_Station = data; + else + dlmstp_set_my_address(DEFAULT_MAC_ADDRESS); + /* FIXME: implement your data storage */ + data = 127; /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + if ((data <= 127) && (data >= MSTP_Port.This_Station)) + MSTP_Port.Nmax_master = data; + else + dlmstp_set_max_master(DEFAULT_MAX_MASTER); + /* FIXME: implement your data storage */ + data = 1; + /* I2C_Read_Byte( + EEPROM_DEVICE_ADDRESS, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + if (data >= 1) + MSTP_Port.Nmax_info_frames = data; + else + dlmstp_set_max_info_frames(DEFAULT_MAX_INFO_FRAMES); +} + +void dlmstp_cleanup(void) +{ + /* nothing to do for static buffers */ +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_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 */ + int bytes_sent = 0; + unsigned npdu_len = 0; + uint8_t frame_type = 0; + uint8_t destination = 0; /* destination address */ + BACNET_ADDRESS src; + unsigned i = 0; /* loop counter */ + + if (MSTP_Port.TxReady == false) { + if (npdu_data->data_expecting_reply) + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + else + MSTP_Port.TxFrameType = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + + /* load destination MAC address */ + if (dest && dest->mac_len == 1) { + destination = dest->mac[0]; + } else { + return -2; + } + dlmstp_get_my_address(&src); + if ((8 /* header len */ + pdu_len) > MAX_MPDU) { + return -4; + } + bytes_sent = MSTP_Create_Frame( + (uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), + MSTP_Port.TxFrameType, + destination, MSTP_Port.This_Station, pdu, pdu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + MSTP_Packets++; + } + + return bytes_sent; +} + +void dlmstp_task(void) +{ + uint8_t bytes_remaining; + bool received_frame; + + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + do { + bytes_remaining = RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + received_frame = MSTP_Port.ReceivedValidFrame || + MSTP_Port.ReceivedInvalidFrame; + if (received_frame) + break; + } while (bytes_remaining); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + }; + } + /* see if there is a packet available, and a place + to put the reply (if necessary) and process it */ + if (Receive_Buffer.ready && !MSTP_Port.TxReady) { + if (Receive_Buffer.pdu_len) { + MSTP_Packets++; + npdu_handler(&Receive_Buffer.address, + &Receive_Buffer.pdu[0], Receive_Buffer.pdu_len); + } + Receive_Buffer.ready = false; + } + + return; +} + +void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ /* amount of PDU data */ + /* PDU is already in the Receive_Buffer */ + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + Receive_Buffer.ready = true; +} + +void dlmstp_set_my_address(uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + MSTP_Port.This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > MSTP_Port.Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_my_address(void) +{ + return MSTP_Port.This_Station; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames(uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + MSTP_Port.Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +unsigned dlmstp_max_info_frames(void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master(uint8_t max_master) +{ + if (max_master <= 127) { + if (MSTP_Port.This_Station <= max_master) { + MSTP_Port.Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master(void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.h b/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.h new file mode 100644 index 00000000..1343d7a8 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.h @@ -0,0 +1,110 @@ +/*####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 DLMSTP_H +#define DLMSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* defines specific to MS/TP */ +#define MAX_HEADER (2+1+1+1+2+1+2+1) +#define MAX_MPDU (MAX_HEADER+MAX_PDU) + +typedef struct dlmstp_packet { + bool ready; /* true if ready to be sent or received */ + BACNET_ADDRESS address; /* source address */ + uint8_t frame_type; /* type of message */ + unsigned pdu_len; /* packet length */ + uint8_t pdu[MAX_MPDU]; /* packet */ +} DLMSTP_PACKET; + +/* number of MS/TP tx/rx packets */ +extern uint16_t MSTP_Packets; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void dlmstp_reinit(void); + void dlmstp_init(void); + void dlmstp_cleanup(void); + void dlmstp_millisecond_timer(void); + void dlmstp_task(void); + + /* returns number of bytes sent on success, negative on failure */ + int dlmstp_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 */ + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + void dlmstp_set_max_info_frames(uint8_t max_info_frames); + unsigned dlmstp_max_info_frames(void); + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + void dlmstp_set_max_master(uint8_t max_master); + uint8_t dlmstp_max_master(void); + + /* MAC address for MS/TP */ + void dlmstp_set_my_address(uint8_t my_address); + uint8_t dlmstp_my_address(void); + + /* BACnet address used in datalink */ + void dlmstp_get_my_address(BACNET_ADDRESS * my_address); + void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest); /* destination address */ + + /* MS/TP state machine functions */ + uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/hardware.h b/bacnet-stack-0-3-0/ports/pic18f6720/hardware.h new file mode 100644 index 00000000..a60744d5 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/hardware.h @@ -0,0 +1,271 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 HARDWARE_H +#define HARDWARE_H + +#include +#include +#include + +/* PORTA.0 Photocell Input PORTA.1 LED Row6 PORTA.2 LED Row5 PORTA.3 LED + * Row4 PORTA.4 Square Wave input from RTC PORTA.5 LCD RW PORTB.0 Zero + * Cross PORTB.1 USB RXF# PORTB.2 USB TXE# PORTB.3 Keypad Row Enable + * (74HC373 Output Control) PORTB.4 Keypad Row Gate (74HC373 Gate) + * PORTB.5 Switch Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 + * ICD connection PORTB.7 ICD connection PORTC.0 Pilot Latch PORTC.1 + * Pilot Output Enable (low) PORTC.2 Piezo PORTC.3 I2C clock PORTC.4 I2C + * data PORTC.5 RS232 enable (low) PORTC.6 RS232 Tx PORTC.7 RS232 Rx + * PORTD.0 Data bus PORTD.1 Data bus PORTD.2 Data bus PORTD.3 Data bus + * PORTD.4 Data bus PORTD.5 Data bus PORTD.6 Data bus PORTD.7 Data bus + * PORTE.0 USB RD PORTE.1 USB WR PORTE.2 LCD RS PORTE.3 485 transmit + * enable PORTE.4 Relay data latch PORTE.5 Switch Input Clock PORTE.6 + * Switch Input High/Low PORTE.7 Switch Input Data PORTF.0 LED Row2 + * PORTF.1 LED Row1 PORTF.2 LED Col5 PORTF.3 LED Col4 PORTF.4 LED Col3 + * PORTF.5 LED Col2 PORTF.6 LED Col1 PORTF.7 LED Col0 PORTG.0 485 receive + * enable PORTG.1 485 Tx PORTG.2 485 Rx PORTG.3 LCD E PORTG.4 LED Row0 */ +#define LCD_BUSY PORTDbits.RD7 +#define LCD_E PORTGbits.RG3 +#define LCD_RW PORTAbits.RA5 +#define LCD_RS PORTEbits.RE2 +#define LCD_DATA PORTD +#define LCD_TRIS TRISD + +#define PILOT_LATCH PORTCbits.RC0 +#define PILOT_ENABLE PORTCbits.RC1 +#define PILOT_PORT PORTD +#define PILOT_PORT_TRIS TRISD + +#define PIEZO PORTCbits.RC2 +#define PIEZO_ON() TRISCbits.TRISC2 = 0 +#define PIEZO_OFF() TRISCbits.TRISC2 = 1 + +#define RS232_ENABLE PORTCbits.RC5 + +#define RS485_TX_ENABLE PORTEbits.RE3 +#define RS485_RX_DISABLE PORTGbits.RG0 + +#define SWITCH_LOAD PORTBbits.RB5 +#define SWITCH_CLK PORTEbits.RE5 +#define SWITCH_COM PORTEbits.RE6 +#define SWITCH_DATA PORTEbits.RE7 + +#define LEDPORT PORTF +#define LEDTRIS TRISF +#define LED_ROW1 PORTGbits.RG4 +#define LED_ROW2 PORTFbits.RF1 +#define LED_ROW3 PORTFbits.RF0 +#define LED_ROW4 PORTAbits.RA3 +#define LED_ROW5 PORTAbits.RA2 +#define LED_ROW6 PORTAbits.RA1 + +#define RELAY_PORT PORTD +#define RELAY_PORT_TRIS TRISD +#define RELAY_LATCH PORTEbits.RE4 + +#define KEYPAD_DATA PORTD +#define KEYPAD_TRIS TRISD +#define KEYPAD_COL_LATCH PORTBbits.RB5 +#define KEYPAD_ROW_ENABLE PORTBbits.RB3 +#define KEYPAD_ROW_LATCH PORTBbits.RB4 +#define KEYPAD_ROW1 0 b00000001 +#define KEYPAD_ROW2 0 b00000010 +#define KEYPAD_ROW3 0 b00000100 +#define KEYPAD_ROW4 0 b00001000 +#define KEYPAD_ROW5 0 b00010000 +#define KEYPAD_ROW6 0 b00100000 + +#define USB_RD_EMPTY PORTBbits.RB1 +#define USB_WR_FULL PORTBbits.RB2 +#define USB_RD PORTEbits.RE0 +#define USB_WR PORTEbits.RE1 +#define USB_PORT PORTD +#define USB_PORT_TRIS TRISD + +#define ZERO_CROSS PORTBbits.RB0 + +#define PORT_A_TRIS_MASK 0x11 /* 0b00010001 */ +#define PORT_B_TRIS_MASK 0xC7 /* 0b11000111 */ +#define PORT_C_TRIS_MASK 0x9C /* 0b10011100 */ +#define PORT_D_TRIS_MASK 0xFF /* 0b11111111 */ +#define PORT_E_TRIS_MASK 0x88 /* 0b10001000 */ +#define PORT_F_TRIS_MASK 0x00 /* 0b00000000 */ +#define PORT_G_TRIS_MASK 0x04 /* 0b00000100 */ + +#define TURN_OFF_COMPARATORS() CMCON = 0x07 + +#define SHORT_BEEP 50 +#define LONG_BEEP 250 + +#define CLICK() Hardware_Sound_Piezo(SHORT_BEEP); +#define BEEP() Hardware_Sound_Piezo(LONG_BEEP); + +typedef union { + struct { + uint8_t:1; + uint8_t:1; + uint8_t Thursday:1; + uint8_t Wednesday:1; + uint8_t Tuesday:1; + uint8_t Monday:1; + uint8_t Program:1; + uint8_t Run:1; + + uint8_t:1; + uint8_t:1; + uint8_t Input1:1; + uint8_t Input2:1; + uint8_t Input3:1; + uint8_t Input4:1; + uint8_t Input5:1; + uint8_t Input6:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Input7:1; + uint8_t:1; + uint8_t:1; + uint8_t Input8:1; + uint8_t Photocell:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Remote:1; + uint8_t Relay8:1; + + uint8_t:1; + uint8_t:1; + uint8_t:1; + uint8_t Relay7:1; + uint8_t Relay6:1; + uint8_t Relay5:1; + uint8_t Relay4:1; + uint8_t Relay3:1; + + uint8_t:1; + uint8_t:1; + uint8_t Relay2:1; + uint8_t Relay1:1; + uint8_t Holiday:1; + uint8_t Sunday:1; + uint8_t Saturday:1; + uint8_t Friday:1; + }; + struct { + uint8_t row1; + uint8_t row2; + uint8_t row3; + uint8_t row4; + uint8_t row5; + uint8_t row6; + }; +} LED_REGS; + +union SWITCH_REGS { + struct { + uint8_t All_On:1; + uint8_t All_Off:1; + uint8_t Addr:4; + uint8_t Pilot_Fault:1; + uint8_t Master:1; + }; + uint8_t Sw_Byte; +}; + +enum INT_STATE { INT_DISABLED, INT_ENABLED, INT_RESTORE }; + +#define RESTART_WDT() { _asm CLRWDT _endasm } + +/* ************************************************************************* + define ENABLE_GLOBAL_INT() INTCONbits.GIE = 1 £ + #define DISABLE_GLOBAL_INT() INTCONbits.GIE = 0 £ + #define ENABLE_PERIPHERAL_INT() INTCONbits.PEIE = 1 £ + #define DISABLE_PERIPHERAL_INT() INTCONbits.PEIE = 0 + *************************************************************************** */ +#define ENABLE_HIGH_INT() INTCONbits.GIE = 1 +#define DISABLE_HIGH_INT() INTCONbits.GIE = 0 + +#define ENABLE_LOW_INT() INTCONbits.PEIE = 1 +#define DISABLE_LOW_INT() INTCONbits.PEIE = 0 + +#define ENABLE_TIMER0_INT() INTCONbits.TMR0IE = 1 +#define DISABLE_TIMER0_INT() INTCONbits.TMR0IE = 0 + +#define ENABLE_TIMER2_INT() PIE1bits.TMR2IE = 1 +#define DISABLE_TIMER2_INT() PIE1bits.TMR2IE = 0 + +#define ENABLE_TIMER4_INT() PIE3bits.TMR4IE = 1 +#define DISABLE_TIMER4_INT() PIE3bits.TMR4IE = 0 + +#define ENABLE_CCP2_INT() PIE2bits.CCP2IE = 1 +#define DISABLE_CCP2_INT() PIE2bits.CCP2IE = 0 + +#define ENABLE_CCP1_INT() PIE1bits.CCP1IE = 1 +#define DISABLE_CCP1_INT() PIE1bits.CCP1IE = 0 + +#define ENABLE_ABUS_INT() PIE1bits.SSPIE = 1 +#define DISABLE_ABUS_INT() PIE1bits.SSPIE = 0 +#define CLEAR_ABUS_FLAG() PIR1bits.SSPIF = 0 + +#define SETUP_CCP1(x) CCP1CON = x +#define SETUP_CCP2(x) CCP2CON = x + +#define DISABLE_RX_INT() PIE1bits.RCIE = 0 +#define ENABLE_RX_INT() PIE1bits.RCIE = 1 + +#define DISABLE_TX_INT() PIE1bits.TXIE = 0 +#define ENABLE_TX_INT() PIE1bits.TXIE = 1 + +#if CLOCKSPEED == 20 +#define DELAY_US(x) { _asm \ + MOVLW x \ + LOOP: \ + NOP \ + NOP \ + DECFSZ WREG, 1, 0 \ + BRA LOOP \ + _endasm } +#endif + +#define setup_timer4(mode, period, postscale) \ + T4CON = (mode | (postscale - 1) << 3); \ + PR4 = period + +#define setup_timer2(mode, period, postscale) \ + T2CON = (mode | (postscale - 1) << 3); \ + PR2 = period + + +/* Global Vars */ +extern volatile LED_REGS LEDS; +extern volatile LED_REGS Blink; +extern uint8_t Piezo_Timer; +extern volatile bool DataPortLocked; + +#endif /* HARDWARE_H */ diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/isr.c b/bacnet-stack-0-3-0/ports/pic18f6720/isr.c new file mode 100644 index 00000000..5a84fc46 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/isr.c @@ -0,0 +1,190 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include "stdint.h" +#include "hardware.h" +#include "rs485.h" +#include "dlmstp.h" + +/* from main.c */ +extern volatile uint8_t Milliseconds; + +void InterruptHandlerHigh(void); +void InterruptHandlerLow(void); +void Interrupt_Timer2(void); +void Interrupt_Timer3(void); +void Interrupt_Timer4(void); +void Interrupt_USART_Rx(void); +void Interrupt_USART_Tx(void); +void Interrupt_CCP2(void); +void INT0_Interrupt(void); + +#pragma code InterruptVectorHigh = 0x08 +void InterruptVectorHigh(void) +{ + /* jump to interrupt routine */ +_asm goto InterruptHandlerHigh _endasm} +#pragma code +#pragma code InterruptVectorLow = 0x18 +void InterruptVectorLow(void) +{ + /* jump to interrupt routine */ +_asm goto InterruptHandlerLow _endasm} +#pragma code +#pragma interrupt InterruptHandlerHigh +void InterruptHandlerHigh(void) +{ +#if 0 + /* check for USART Rx int */ + if ((PIR1bits.RCIF) && (PIE1bits.RCIE)) { + if ((RCSTA1bits.FERR) || (RCSTA1bits.OERR)) { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } else if (Comstat.Rx_Bytes++ < RX_BUFFER_SIZE - 1) { + Rx_Buffer[Comstat.RxHead++] = RCREG1; + + /* Stick a Null on the end to let us use str functions on our + * buffer */ + Rx_Buffer[Comstat.RxHead] = 0; + } else { + Comstat.Rx_Bufferoverrun = TRUE; + PIE1bits.RC1IE = 0; /* Disable Interrupt on receipt */ + } + } +#endif + + /* check for timer0 int */ + if ((INTCONbits.TMR0IF) && (INTCONbits.TMR0IE)) { + INTCONbits.TMR0IF = 0; + } +} + +#pragma interruptlow InterruptHandlerLow save = PROD, section(".tmpdata"), TABLAT, TBLPTR, section \ + ("MATH_DATA") + +void InterruptHandlerLow(void) +{ + /* check for timer2 int */ + if ((PIR1bits.TMR2IF) && (PIE1bits.TMR2IE)) { + PIR1bits.TMR2IF = 0; + Interrupt_Timer2(); + } + + /* check for timer3 int */ + if ((PIR2bits.TMR3IF) && (PIE2bits.TMR3IE)) { + PIR2bits.TMR3IF = 0; + Interrupt_Timer3(); + } + + /* check for timer4 int */ + if ((PIR3bits.TMR4IF) && (PIE3bits.TMR4IE)) { + PIR3bits.TMR4IF = 0; + dlmstp_millisecond_timer(); + Interrupt_Timer4(); + } + + /* check for compare int */ + if ((PIR2bits.CCP2IF) && (PIE2bits.CCP2IE)) { + PIR2bits.CCP2IF = 0; + Interrupt_CCP2(); + } + + /* check for USART Tx int */ + if ((PIR3bits.TX2IF) && (PIE3bits.TX2IE)) { + RS485_Interrupt_Tx(); + } + + /* check for USART Rx int */ + if ((PIR3bits.RC2IF) && (PIE3bits.RC2IE)) { + RS485_Interrupt_Rx(); + } + +/* Unused Interrupts + //check for timer1 int + if ((PIR1bits.TMR1IF) && (PIE1bits.TMR1IE)) + { + PIR1bits.TMR1IF = 0; + Interrupt_Timer1(); + } + + //check for compare int + if ((PIR1bits.CCP1IF) && (PIE1bits.CCP1IE)) + { + PIR1bits.CCP1IF = 0; + Interrupt_CCP1(); + } + + //check for compare int + if ((PIR3bits.CCP3IF) && (PIE3bits.CCP3IE)) + { + PIR3bits.CCP3IF = 0; + Interrupt_CCP3(); + } + + //check for compare int + if ((PIR3bits.CCP4IF) && (PIE3bits.CCP4IE)) + { + PIR3bits.CCP4IF = 0; + + Interrupt_CCP4(); + } + + //check for AD int + if ((PIR1bits.ADIF) && (PIE1bits.ADIE)) + { + PIR1bits.ADIF = 0; + Interrupt_ADC(); + } + + //check for MSSP int + if ((PIR1bits.SSPIF) && (PIE1bits.SSPIE)) + { + PIR1bits.SSPIF = 0; + Interrupt_SSP(); + } + +*/ +} + +void Interrupt_Timer2(void) +{ +} + +void Interrupt_Timer3(void) +{ +} + +/* Timer4 is set to go off every 1ms. This is our system tick */ +void Interrupt_Timer4(void) +{ + /* Milisecond is our system tick */ + if (Milliseconds < 0xFF) + ++Milliseconds; +} + +void Interrupt_CCP2(void) +{ + +} diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/main.c b/bacnet-stack-0-3-0/ports/pic18f6720/main.c new file mode 100644 index 00000000..3523a850 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/main.c @@ -0,0 +1,294 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include /* for memmove */ +#include +#include +#include +#include "stdint.h" +#include "hardware.h" +/* BACnet */ +#include "apdu.h" +#include "datalink.h" +#include "dcc.h" +#include "handlers.h" +#include "iam.h" +#include "txbuf.h" + +/* chip configuration data */ +/* define this to enable ICD */ +/* #define USE_ICD */ + +/* Configuration Bits */ +#pragma config OSC = HS, OSCS = OFF +#pragma config PWRT = ON +#pragma config BOR = ON, BORV = 27 +#pragma config CCP2MUX = ON +#pragma config STVR = ON +#pragma config LVP = OFF +#pragma config CP0 = OFF +#pragma config CP1 = OFF +#pragma config CP2 = OFF +#pragma config CP3 = OFF +#pragma config CP4 = OFF +#pragma config CP5 = OFF +#pragma config CP6 = OFF +#pragma config CP7 = OFF +#pragma config CPB = OFF +#pragma config CPD = OFF +#pragma config WRT0 = OFF +#pragma config WRT1 = OFF +#pragma config WRT2 = OFF +#pragma config WRT3 = OFF +#pragma config WRT4 = OFF +#pragma config WRT5 = OFF +#pragma config WRT6 = OFF +#pragma config WRT7 = OFF +#pragma config WRTB = OFF +#pragma config WRTC = OFF +#pragma config WRTD = OFF +#pragma config EBTR0 = OFF +#pragma config EBTR1 = OFF +#pragma config EBTR2 = OFF +#pragma config EBTR3 = OFF +#pragma config EBTR4 = OFF +#pragma config EBTR5 = OFF +#pragma config EBTR6 = OFF +#pragma config EBTR7 = OFF +#pragma config EBTRB = OFF + +#ifdef USE_ICD +#pragma config WDT = OFF, WDTPS = 128 +#pragma config DEBUG = ON +#else +#pragma config WDT = ON, WDTPS = 128 +#pragma config DEBUG = OFF +#endif /* USE_ICD */ + +volatile uint8_t Milliseconds = 0; +volatile uint8_t Zero_Cross_Timeout = 0; + +static void BACnet_Service_Handlers_Init(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, + handler_reinitialize_device); +#if 0 + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + apdu_set_unconfirmed_handler + (SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + handler_timesync_utc); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + handler_timesync); +#endif + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler + (SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); +} + +void Reinitialize(void) +{ + uint8_t i; + char name = 0; + + _asm reset _endasm return; +} + +void Global_Int(enum INT_STATE state) +{ + static uint8_t intstate = 0; + + switch (state) { + case INT_DISABLED: + intstate >>= 2; + intstate |= (INTCON & 0xC0); + break; + case INT_ENABLED: + INTCONbits.GIE = 1; + INTCONbits.PEIE = 1; + intstate <<= 2; + break; + case INT_RESTORE: + INTCON |= (intstate & 0xC0); + intstate <<= 2; + break; + default: + break; + } +} + +void Hardware_Initialize(void) +{ + /* PORTA.0 Input - Photocell PORTA.1 Output - LED Row6 PORTA.2 Output + * - LED Row5 PORTA.3 Output - LED Row4 PORTA.4 Input - Square Wave + * input from RTC PORTA.5 Output - LCD RW */ + TRISA = 0xD1; + + /* PORTB.0 Input - Zero Cross PORTB.1 Input - USB RXF# PORTB.2 Input + * USB TXE# PORTB.3 Output - Keypad Row Enable (74HC373 Output Control) + * PORTB.4 Output Keypad Row Gate (74HC373 Gate) PORTB.5 Output Switch + * Input Latch & Keypad Column Gate (74HC373 Gate) PORTB.6 Input - ICD + * connection PORTB.7 Input - ICD connection */ + TRISB = 0xC7; + + /* PORTC.0 Output - Pilot Latch PORTC.1 Output - Pilot Output Enable + * (low) PORTC.2 I/O - Piezo PORTC.3 Input - I2C clock PORTC.4 Input + * I2C data PORTC.5 Output RS232 enable (low) PORTC.6 Output - RS232 Tx + * PORTC.7 Input - RS232 Rx */ + TRISC = 0x9C; + + /* PORTD.0 I/O - Data bus PORTD.1 I/O - Data bus PORTD.2 I/O - Data + * bus PORTD.3 I/O - Data bus PORTD.4 I/O - Data bus PORTD.5 I/O - Data + * bus PORTD.6 I/O - Data bus PORTD.7 I/O - Data bus */ + TRISD = 0xFF; + + /* PORTE.0 Input - USB RD PORTE.1 Input - USB WR PORTE.2 Output - LCD + * RS PORTE.3 Output - 485 transmit enable PORTE.4 Output - Relay data + * latch PORTE.5 Output Switch Input Clock PORTE.6 Output - Switch + * Input High/Low PORTE.7 Input Switch Input Data */ + TRISE = 0x83; + + /* PORTF.0 Output - LED Row2 PORTF.1 Output - LED Row1 PORTF.2 Output + * - LED Col5 PORTF.3 Output - LED Col4 PORTF.4 Output - LED Col3 + * PORTF.5 Output - LED Col2 PORTF.6 Output - LED Col1 PORTF.7 Output + * LED Col0 */ + TRISF = 0x00; + + /* PORTG.0 Output - 485 receive enable PORTG.1 Output - 485 Tx PORTG.2 + * Input 485 Rx PORTG.3 Output - LCD E PORTG.4 Output - LED Row0 */ + TRISG = 0xE6; + + /* The initial state of the keypad enables and latches */ + KEYPAD_ROW_ENABLE = 1; + KEYPAD_ROW_LATCH = 0; + KEYPAD_COL_LATCH = 1; + + RELAY_LATCH = 0; + + /* Setup to read the switch inputs */ + SWITCH_COM = 1; + + /* Enable the RS232 transmitter */ + RS232_ENABLE = 0; + + /* Turn all leds off. These are the hardware pins */ + LED_ROW1 = 1; + LED_ROW2 = 1; + LED_ROW3 = 1; + LED_ROW4 = 1; + LED_ROW5 = 1; + LED_ROW6 = 1; + LEDPORT = 0x03; + + /* The initial values for the signals to the LCD */ + LCD_E = 1; + LCD_RW = 1; + LCD_RS = 1; + + /* The following gives us a PWM frequency of 1.990KHz with a 50% duty + * cycle It also serves to multiplex the LEDs. */ + PIEZO_OFF(); + CCPR1L = 0x4E; + CCP1CON = 0x2F; + setup_timer2(6, 156, 2); + PIE1bits.TMR2IE = 1; + + /* We will use Timer4 as our system tick timer. Our system tick is set + * to 1ms. Hold off on enabling the int. */ + setup_timer4(5, 250, 5); + + /* Setup our interrupt priorities */ + RCONbits.IPEN = 1; + IPR1 = 0; + IPR2 = 0; + IPR3 = 0; + + /* Setup TMR0 to be high priority */ + INTCON2 = 0xFC; + INTCON3 = 0; + + /* USART 1 high priority */ + IPR1bits.RC1IP = 1; + IPR1bits.TX1IP = 1; + + /* Finally enable our ints */ + Global_Int(INT_ENABLED); +} + +void Initialize_Variables(void) +{ + /* Check to see if we need to initialize our eeproms */ + ENABLE_TIMER4_INT(); + /* interrupts must be enabled before we read our inputs */ + Global_Int(INT_ENABLED); + BACnet_Service_Handlers_Init(); + dlmstp_init(); + /* Start our time from now */ + Milliseconds = 0; +} + +void MainTasks(void) +{ + static uint16_t millisecond_counter = 0; + /* Handle our millisecond counters */ + while (Milliseconds) { + millisecond_counter++; + --Milliseconds; + } + /* Handle our seconds counters */ + if (millisecond_counter > 1000) { + millisecond_counter -= 1000; + dcc_timer_seconds(1); + } +} + +void main(void) +{ + RCONbits.NOT_POR = 1; + RCONbits.NOT_RI = 1; + Hardware_Initialize(); + Initialize_Variables(); + /* Handle anything that needs to be done on powerup */ + /* Greet the BACnet world! */ + iam_send(&Handler_Transmit_Buffer[0]); + /* Main loop */ + while (TRUE) { + RESTART_WDT(); + dlmstp_task(); + MainTasks(); + Global_Int(INT_ENABLED); + ENABLE_TIMER4_INT(); + } +} diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/mstp.c b/bacnet-stack-0-3-0/ports/pic18f6720/mstp.c new file mode 100644 index 00000000..e586c210 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/mstp.c @@ -0,0 +1,1706 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" + +/* debug print statements */ +#if PRINT_ENABLED +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 1 +#define PRINT_ENABLED_MASTER 0 +#else +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 0 +#define PRINT_ENABLED_MASTER 0 +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define Nretry_token 1 + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +#define Tpostdrive 15 + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +#define Treply_delay 10 + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +#define Treply_timeout 255 + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#define Troff 30 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define Tslot 10 + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +#define Tusage_delay 15 + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +#define Tusage_timeout 20 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +#if PRINT_ENABLED_RECEIVE +char *mstp_receive_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_RECEIVE_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + text = "PREAMBLE"; + break; + case MSTP_RECEIVE_STATE_HEADER: + text = "HEADER"; + break; + case MSTP_RECEIVE_STATE_HEADER_CRC: + text = "HEADER_CRC"; + break; + case MSTP_RECEIVE_STATE_DATA: + text = "DATA"; + break; + default: + break; + } + + return text; +} +#endif + +void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstp_receive_state_text(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, + mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + /* MSTP_RECEIVE_STATE_HEADER_CRC */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER_CRC state, the node validates the CRC on the fixed */ + /* message header. */ + case MSTP_RECEIVE_STATE_HEADER_CRC: + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +#if PRINT_ENABLED +char *mstp_master_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_MASTER_STATE_INITIALIZE: + text = "INITIALIZE"; + break; + case MSTP_MASTER_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_USE_TOKEN: + text = "USE_TOKEN"; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + text = "WAIT_FOR_REPLY"; + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + text = "DONE_WITH_TOKEN"; + break; + case MSTP_MASTER_STATE_NO_TOKEN: + text = "NO_TOKEN"; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + text = "ANSWER_DATA_REQUEST"; + break; + default: + break; + } + + return text; +} +#endif + +#if PRINT_ENABLED +char *mstp_frame_type_text(int type) +{ + char *text = "unknown"; + + switch (type) { + case FRAME_TYPE_TOKEN: + text = "TOKEN"; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + text = "REPLY_TO_POLL_FOR_MASTER"; + break; + case FRAME_TYPE_TEST_REQUEST: + text = "TEST_REQUEST"; + break; + case FRAME_TYPE_TEST_RESPONSE: + text = "TEST_RESPONSE"; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + text = "BACNET_DATA_EXPECTING_REPLY"; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + text = "BACNET_DATA_NOT_EXPECTING_REPLY"; + break; + case FRAME_TYPE_REPLY_POSTPONED: + text = "REPLY_POSTPONED"; + break; + default: + if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && + (type <= FRAME_TYPE_PROPRIETARY_MAX)) + text = "PROPRIETARY"; + break; + } + + return text; +} +#endif + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = (mstp_port->Poll_Station + 1) % + (mstp_port->Nmax_master + 1); + next_this_station = (mstp_port->This_Station + 1) % + (mstp_port->Nmax_master + 1); + next_next_station = (mstp_port->Next_Station + 1) % + (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, + next_this_station, + mstp_port->Next_Station, + next_next_station, + mstp_port->Poll_Station, + next_poll_station, + mstp_port->EventCount, + mstp_port->TokenCount, + mstp_port->SilenceTimer, + mstp_master_state_text(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, + mstp_port->DestinationAddress, + mstp_port->DataLength, + mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstp_frame_type_text(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) || + (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /*mstp_port->ReplyPostponedTimer = 0; */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->SilenceTimer > Tusage_delay) { + /* if we missed our timing deadline, another token will be sent */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } else { + /* don't send it if we are too late in getting out */ + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + /* FIXME: if we knew the APDU type received, we could + see if the next message was that same APDU type */ + if ((mstp_port->SilenceTimer <= Treply_delay) && + mstp_port->TxReady) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + if ((mstp_port->FrameType == + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) + && (mstp_port->TxReady)) { + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], + mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + } + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + else { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* note: This_Station should be set with the MAC address */ +/* note: Nmax_info_frames should be set */ +/* note: Nmax_master should be set */ +void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; +/* mstp_port->ReplyPostponedTimer = 0; */ + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; +#if 0 + /* these are adjustable, so should already be set */ + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; +#endif + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* test stub functions */ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + (void) mstp_port; + (void) buffer; + (void) nbytes; +} + +#define RING_BUFFER_DATA_SIZE 1 +#define RING_BUFFER_SIZE MAX_MPDU +static RING_BUFFER Test_Buffer; +static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE]; +static void Load_Input_Buffer(uint8_t * buffer, size_t len) +{ + static bool initialized = false; /* tracks our init */ + + + if (!initialized) { + initialized = true; + Ringbuf_Init(&Test_Buffer, + (char *) Test_Buffer_Data, + RING_BUFFER_DATA_SIZE, RING_BUFFER_SIZE); + } + /* empty any the existing data */ + while (!Ringbuf_Empty(&Test_Buffer)) { + (void) Ringbuf_Pop_Front(&Test_Buffer); + } + + if (buffer) { + while (len) { + (void) Ringbuf_Put(&Test_Buffer, (char *) buffer); + len--; + buffer++; + } + } +} + +void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + char *data; + if (!Ringbuf_Empty(&Test_Buffer) && mstp_port && + (mstp_port->DataAvailable == false)) { + data = Ringbuf_Pop_Front(&Test_Buffer); + if (data) { + mstp_port->DataRegister = *data; + mstp_port->DataAvailable = true; + } + } +} + +void testReceiveNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { 0 }; + uint8_t data[MAX_MPDU - 8 /*header */ - 2 /*CRC*/] = { 0 }; + + MSTP_Init(&mstp_port, my_mac); + + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + mstp_port.SilenceTimer = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 1); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, FrameType == FRAME_TYPE_TOKEN); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 2); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DestinationAddress == 0x10); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 3); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.SourceAddress == my_mac); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 4); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + ct_test(pTest, HeaderCRC == 0x73); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + ct_test(pTest, mstp_port.HeaderCRC == 0x55); + /* NotForUs */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data, 0, sizeof(data)); + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, my_mac, /* destination */ + my_mac, /* source */ + data, /* data */ + sizeof(data)); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + ct_test(pTest, mstp_port.DataLength == sizeof(data)); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + return; +} + +void testMasterNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + + MSTP_Init(&mstp_port, my_mac); + ct_test(pTest, mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE); + +} + +#endif + +#ifdef TEST_MSTP +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("mstp", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testReceiveNodeFSM); + assert(rc); + rc = ct_addTestFunction(pTest, testMasterNodeFSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/mstp.h b/bacnet-stack-0-3-0/ports/pic18f6720/mstp.h new file mode 100644 index 00000000..3a658cdd --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/mstp.h @@ -0,0 +1,246 @@ +/*####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 MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_HEADER_CRC = 3, + MSTP_RECEIVE_STATE_DATA = 4 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t *InputBuffer; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ +/* note: we always send a reply postponed since a message other than + the reply may be in the transmit queue */ +/* uint16_t ReplyPostponedTimer; */ + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 +#define DEFAULT_MAC_ADDRESS 127 + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround 40 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port); + void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/rs485.c b/bacnet-stack-0-3-0/ports/pic18f6720/rs485.c new file mode 100644 index 00000000..f2d7daac --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/rs485.c @@ -0,0 +1,380 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + +/* The module handles sending data out the RS-485 port */ +/* and handles receiving data from the RS-485 port. */ +/* Customize this file for your specific hardware */ +#include +#include +#include +#include +#include "hardware.h" +#include "mstp.h" +#include "rs485.h" + +/* public port info */ +extern volatile struct mstp_port_struct_t MSTP_Port; + +/* the baud rate is adjustable */ +uint32_t RS485_Baud_Rate = 38400; + +/* the ISR and other use this for status and control */ +COMSTAT RS485_Comstat; + +/*#pragma udata MSTPPortData + */ +/* the buffer for receiving characters */ +volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; + +/* UART transmission buffer and index */ +volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; + +/**************************************************************************** +* DESCRIPTION: Transmits a frame using the UART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + uint16_t i = 0; /* loop counter */ + uint8_t turnaround_time; + + if (!buffer) + return; + + /* bounds check */ + if (nbytes >= sizeof(RS485_Tx_Buffer)) + return; + + /* buffer is full. Wait for ISR to transmit. */ + while (RS485_Comstat.Tx_Bytes) { + }; + + /* wait 40 bit times since reception */ + if (RS485_Baud_Rate == 9600) + turnaround_time = 4; + else if (RS485_Baud_Rate == 19200) + turnaround_time = 2; + else + turnaround_time = 1; + + while (mstp_port->SilenceTimer < turnaround_time) { + }; + + RS485_Comstat.TxHead = 0; + memcpy((void *) &RS485_Tx_Buffer[0], (void *) buffer, nbytes); +#if 0 + for (i = 0; i < nbytes; i++) { + /* put the data into the buffer */ + RS485_Tx_Buffer[i] = *buffer; + buffer++; + } +#endif + RS485_Comstat.Tx_Bytes = nbytes; + /* disable the receiver */ + PIE3bits.RC2IE = 0; + RCSTA2bits.CREN = 0; + /* enable the transceiver */ + RS485_TX_ENABLE = 1; + RS485_RX_DISABLE = 1; + /* enable the transmitter */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 1; + /* per MSTP spec, sort of */ + mstp_port->SilenceTimer = 0; + + return; +} + +/**************************************************************************** +* DESCRIPTION: Checks for data on the receive UART, and handles errors +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t * + mstp_port) +{ + /* check for data */ + if (RS485_Comstat.Rx_Bytes) { + mstp_port->DataRegister = RS485_Rx_Buffer[RS485_Comstat.RxTail]; + if (RS485_Comstat.RxTail >= (sizeof(RS485_Rx_Buffer) - 1)) + RS485_Comstat.RxTail = 0; + else + RS485_Comstat.RxTail++; + RS485_Comstat.Rx_Bytes--; + /* errors? let the state machine know */ + if (RS485_Comstat.Rx_Bufferoverrun) { + RS485_Comstat.Rx_Bufferoverrun = FALSE; + mstp_port->ReceiveError = TRUE; + } + /* We read a good byte */ + else + mstp_port->DataAvailable = TRUE; + } + + return RS485_Comstat.Rx_Bytes; +} + +/* ************************************************************************* + DESCRIPTION: Receives RS485 data stream + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Rx(void) +{ + char dummy; + + if ((RCSTA2bits.FERR) || (RCSTA2bits.OERR)) { + /* Clear the error */ + RCSTA2bits.CREN = 0; + RCSTA2bits.CREN = 1; + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + } else if (RS485_Comstat.Rx_Bytes < sizeof(RS485_Rx_Buffer)) { + RS485_Rx_Buffer[RS485_Comstat.RxHead] = RCREG2; + if (RS485_Comstat.RxHead >= (sizeof(RS485_Rx_Buffer) - 1)) + RS485_Comstat.RxHead = 0; + else + RS485_Comstat.RxHead++; + RS485_Comstat.Rx_Bytes++; + } else { + RS485_Comstat.Rx_Bufferoverrun = TRUE; + dummy = RCREG2; + (void) dummy; + } +} + +/* ************************************************************************* + DESCRIPTION: Transmits a byte using the UART out the RS485 port + + RETURN: none + + ALGORITHM: none + + NOTES: none + *************************************************************************** */ +void RS485_Interrupt_Tx(void) +{ + if (RS485_Comstat.Tx_Bytes) { + /* Get the data byte */ + TXREG2 = RS485_Tx_Buffer[RS485_Comstat.TxHead]; + /* point to the next byte */ + RS485_Comstat.TxHead++; + /* reduce the buffer size */ + RS485_Comstat.Tx_Bytes--; + } else { + /* wait for the USART to be empty */ + while (!TXSTA2bits.TRMT); + /* disable this interrupt */ + PIE3bits.TX2IE = 0; + /* enable the receiver */ + RS485_TX_ENABLE = 0; + RS485_RX_DISABLE = 0; + /* FIXME: might not be necessary + */ + PIE3bits.RC2IE = 1; + RCSTA2bits.CREN = 1; + } +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate(void) +{ + return RS485_Baud_Rate; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate(uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud_Rate = baud; + break; + default: + valid = false; + break; + } + + if (valid) { + /* FIXME: store the baud rate */ + /* I2C_Write_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize_Port(void) +{ + + /* Reset USART registers to POR state */ + TXSTA2 = 0; + RCSTA2 = 0; + /* configure USART for receiving */ + /* since the TX will handle setting up for transmit */ + RCSTA2bits.CREN = 1; + /* Interrupt on receipt */ + PIE3bits.RC2IE = 1; + /* enable the transmitter, disable its interrupt */ + TXSTA2bits.TXEN = 1; + PIE3bits.TX2IE = 0; + /* setup USART Baud Rate Generator */ + /* see BAUD RATES FOR ASYNCHRONOUS MODE in Data Book */ + /* Fosc=20MHz + BRGH=1 BRGH=0 + Rate SPBRG Rate SPBRG + ------- ----- ------- ----- + 9615 129 9469 32 + 19230 64 19530 15 + 37878 32 78130 3 + 56818 21 104200 2 + 113630 10 312500 0 + 250000 4 + 625000 1 + 1250000 0 + */ + switch (RS485_Baud_Rate) { + case 19200: + SPBRG2 = 64; + TXSTA2bits.BRGH = 1; + break; + case 38400: + SPBRG2 = 32; + TXSTA2bits.BRGH = 1; + break; + case 57600: + SPBRG2 = 21; + TXSTA2bits.BRGH = 1; + break; + case 76800: + SPBRG2 = 3; + TXSTA2bits.BRGH = 0; + break; + case 115200: + SPBRG2 = 10; + TXSTA2bits.BRGH = 1; + break; + case 9600: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + break; + default: + SPBRG2 = 129; + TXSTA2bits.BRGH = 1; + RS485_Set_Baud_Rate(9600); + break; + } + /* select async mode */ + TXSTA2bits.SYNC = 0; + /* enable transmitter */ + TXSTA2bits.TXEN = 1; + /* serial port enable */ + RCSTA2bits.SPEN = 1; + /* since we are using RS485, + we need to explicitly say + transmit enable or not */ + RS485_RX_DISABLE = 0; + RS485_TX_ENABLE = 0; +} + +/**************************************************************************** +* DESCRIPTION: Disables the RS485 hardware +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Disable_Port(void) +{ + RCSTA2 &= 0x4F; /* Disable the receiver */ + TXSTA2bits.TXEN = 0; /* and transmitter */ + PIE3 &= 0xCF; /* Disable both interrupts */ +} + +void RS485_Reinit(void) +{ + RS485_Set_Baud_Rate(38400); +} + +/**************************************************************************** +* DESCRIPTION: Initializes the data and the port +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize(void) +{ + /* Init the Rs485 buffers */ + RS485_Comstat.RxHead = 0; + RS485_Comstat.RxTail = 0; + RS485_Comstat.Rx_Bytes = 0; + RS485_Comstat.Rx_Bufferoverrun = FALSE; + RS485_Comstat.TxHead = 0; + RS485_Comstat.TxTail = 0; + RS485_Comstat.Tx_Bytes = 0; + + /* FIXME: read the data from storage */ + /* I2C_Read_Block( + EEPROM_DEVICE_ADDRESS, + (char *)&RS485_Baud_Rate, + sizeof(RS485_Baud_Rate), + EEPROM_MSTP_BAUD_RATE_ADDR); */ + + RS485_Initialize_Port(); +} diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/rs485.h b/bacnet-stack-0-3-0/ports/pic18f6720/rs485.h new file mode 100644 index 00000000..b4dc293c --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/rs485.h @@ -0,0 +1,84 @@ +/*####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 RS485_H +#define RS485_H + +#include +#include "mstp.h" + +typedef struct { + uint8_t RxHead; + uint8_t RxTail; + uint8_t Rx_Bytes; + uint8_t TxHead; + uint8_t TxTail; + uint8_t Tx_Bytes; + uint8_t Rx_Bufferoverrun:1; + uint8_t Tx_Bufferoverrun:1; +} COMSTAT; + +extern COMSTAT RS485_Comstat; +extern volatile uint8_t RS485_Rx_Buffer[MAX_MPDU]; +extern volatile uint8_t RS485_Tx_Buffer[MAX_MPDU]; +extern uint32_t RS485_Baud_Rate; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Reinit(void); + void RS485_Initialize(void); + + void RS485_Disable(void); + + void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + uint8_t RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + + void RS485_Interrupt_Rx(void); + + void RS485_Interrupt_Tx(void); + + uint32_t RS485_Get_Baud_Rate(void); + bool RS485_Set_Baud_Rate(uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/stdbool.h b/bacnet-stack-0-3-0/ports/pic18f6720/stdbool.h new file mode 100644 index 00000000..696ffd85 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef char _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/bacnet-stack-0-3-0/ports/pic18f6720/stdint.h b/bacnet-stack-0-3-0/ports/pic18f6720/stdint.h new file mode 100644 index 00000000..e9629b20 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/pic18f6720/stdint.h @@ -0,0 +1,18 @@ +/* Defines the standard integer types that are used in code */ + +#ifndef STDINT_H +#define STDINT_H 1 + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/bacnet-stack-0-3-0/ports/rtos32/bip-init.c b/bacnet-stack-0-3-0/ports/rtos32/bip-init.c new file mode 100644 index 00000000..f223eb15 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/bip-init.c @@ -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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" + +static int interface = SOCKET_ERROR; /* SOCKET_ERROR means no open interface */ + +void bip_set_interface(char *ifname) +{ + /*dummy function - to make the demos compile easier */ + (void) ifname; +} + +/*-----------------------------------*/ +static void Error(const char *Msg) +{ + int Code = WSAGetLastError(); +#ifdef HOST + printf("%s, error code: %i\n", Msg, Code); +#else + printf("%s, error code: %s\n", Msg, xn_geterror_string(Code)); +#endif + exit(1); +} + +#ifndef HOST +/*-----------------------------------*/ +void InterfaceCleanup(void) +{ + if (interface != SOCKET_ERROR) { + xn_interface_close(interface); + interface = SOCKET_ERROR; +#if DEVICE_ID == PRISM_PCMCIA_DEVICE + RTPCShutDown(); +#endif + } +} + +static void NetInitialize(void) +/* initialize the TCP/IP stack */ +{ + int Result; + +#ifndef HOST + if (!RTKDebugVersion()) /* switch of all diagnostics and error messages of RTIP-32 */ + xn_callbacks()->cb_wr_screen_string_fnc = NULL; + +#ifdef RTUSB_VER + RTURegisterCallback(USBAX172); /* ax172 and ax772 drivers */ + RTURegisterCallback(USBAX772); + RTURegisterCallback(USBKeyboard); /* support USB keyboards */ + FindUSBControllers(); /* install USB host controllers */ + Sleep(2000); /* give the USB stack time to enumerate devices */ +#endif + +#ifdef DHCP + XN_REGISTER_DHCP_CLI() /* and optionally the DHCP client */ +#endif + Result = xn_rtip_init(); /* Initialize the RTIP stack */ + if (Result != 0) + Error("xn_rtip_init failed"); + + atexit(InterfaceCleanup); /* make sure the driver is shut down properly */ + RTCallDebugger(RT_DBG_CALLRESET, (DWORD) exit, 0); /* even if we get restarted by the debugger */ + + Result = BIND_DRIVER(MINOR_0); /* tell RTIP what Ethernet driver we want (see netcfg.h) */ + if (Result != 0) + Error("driver initialization failed"); + +#if DEVICE_ID == PRISM_PCMCIA_DEVICE + /* if this is a PCMCIA device, start the PCMCIA driver */ + if (RTPCInit(-1, 0, 2, NULL) == 0) + Error("No PCMCIA controller found"); +#endif + + /* Open the interface */ + interface = + xn_interface_open_config(DEVICE_ID, MINOR_0, ED_IO_ADD, ED_IRQ, + ED_MEM_ADD); + if (interface == SOCKET_ERROR) + Error("xn_interface_open_config failed"); + else { + struct _iface_info ii; +#ifdef BACDL_ETHERNET + BACNET_ADDRESS my_address; + unsigned i; +#endif + xn_interface_info(interface, &ii); + printf + ("Interface opened, MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n", + ii.my_ethernet_address[0], ii.my_ethernet_address[1], + ii.my_ethernet_address[2], ii.my_ethernet_address[3], + ii.my_ethernet_address[4], ii.my_ethernet_address[5]); +#ifdef BACDL_ETHERNET + for (i = 0; i < 6; i++) { + my_address.mac[i] = ii.my_ethernet_address[i]; + } + ethernet_set_my_address(&my_address); +#endif + } + +#if DEVICE_ID == PRISM_PCMCIA_DEVICE || DEVICE_ID == PRISM_DEVICE + xn_wlan_setup(interface, /* iface_no: value returned by xn_interface_open_config() */ + "network name", /* SSID : network name set in the access point */ + "station name", /* Name : name of this node */ + 0, /* Channel : 0 for access points, 1..14 for ad-hoc */ + 0, /* KeyIndex: 0 .. 3 */ + "12345", /* WEP Key : key to use (5 or 13 bytes) */ + 0); /* Flags : see manual and Wlanapi.h for details */ + Sleep(1000); /* wireless devices need a little time before they can be used */ +#endif /* WLAN device */ + +#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */ + Result = xn_autoip(interface, MinIP, MaxIP, NetMask, TargetIP); + if (Result == SOCKET_ERROR) + Error("xn_autoip failed"); + else { + printf("Auto-assigned IP address %i.%i.%i.%i\n", TargetIP[0], + TargetIP[1], TargetIP[2], TargetIP[3]); + /* define default gateway and DNS server */ + xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface, + RT_INF); + xn_set_server_list((DWORD *) DNSServer, 1); + } +#elif defined(DHCP) /* use DHCP */ + { + DHCP_param param[] = { {SUBNET_MASK, 1} + , {DNS_OP, 1} + , {ROUTER_OPTION, 1} + }; + DHCP_session DS; + DHCP_conf DC; + + xn_init_dhcp_conf(&DC); /* load default DHCP options */ + DC.plist = param; /* add MASK, DNS, and gateway options */ + DC.plist_entries = sizeof(param) / sizeof(param[0]); + printf("Contacting DHCP server, please wait...\n"); + Result = xn_dhcp(interface, &DS, &DC); /* contact DHCP server */ + if (Result == SOCKET_ERROR) + Error("xn_dhcp failed"); + memcpy(TargetIP, DS.client_ip, 4); + printf("My IP address is: %i.%i.%i.%i\n", TargetIP[0], TargetIP[1], + TargetIP[2], TargetIP[3]); + } +#else + /* Set the IP address and interface */ + printf("Using static IP address %i.%i.%i.%i\n", TargetIP[0], + TargetIP[1], TargetIP[2], TargetIP[3]); + Result = xn_set_ip(interface, TargetIP, NetMask); + /* define default gateway and DNS server */ + xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, interface, RT_INF); + xn_set_server_list((DWORD *) DNSServer, 1); +#endif + +#else /* HOST defined, run on Windows */ + + WSADATA wd; + Result = WSAStartup(0x0101, &wd); + +#endif + + if (Result != 0) + Error("TCP/IP stack initialization failed"); +} +#endif + +/****************************************************************** +* DESCRIPTION: Converts the byte stored address to an inet address +* RETURN: none +* ALGORITHM: none +* NOTES: none +******************************************************************/ +static void RTIP_To_Network_Address(BYTE * octet_address, + struct in_addr *addr) +{ + uint32_t ip_address = 0; /* for decoding the subnet mask */ + + decode_unsigned32(octet_address, &ip_address); + addr->s_addr = htonl(ip_address); + + return; +} + +static void set_broadcast_address(uint32_t net_address) +{ + long broadcast_address = 0; + long mask = 0; + + /* Note: sometimes INADDR_BROADCAST does not let me get + any unicast messages. Not sure why... */ +#if USE_INADDR + (void) net_address; + bip_set_broadcast_addr(INADDR_BROADCAST); +#else + if (IN_CLASSA(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST; + else if (IN_CLASSB(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST; + else if (IN_CLASSC(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST; + else if (IN_CLASSD(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST; + else + broadcast_address = INADDR_BROADCAST; + bip_set_broadcast_addr(htonl(broadcast_address)); +#endif +} + +bool bip_init(void) +{ + int rv = 0; /* return from socket lib calls */ + struct sockaddr_in sin = { -1 }; + int value = 1; + int sock_fd = -1; + struct in_addr my_addr; + + NetInitialize(); + + RTIP_To_Network_Address(TargetIP, &my_addr); + bip_set_addr(my_addr.s_addr); + set_broadcast_address(my_addr.s_addr); + bip_set_port(0xBAC0); + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(bip_get_port()); + memset(&(sin.sin_zero), '\0', 8); + rv = bind(sock_fd, + (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (rv < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/dlmstp.c b/bacnet-stack-0-3-0/ports/rtos32/dlmstp.c new file mode 100644 index 00000000..4a3adfe8 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/dlmstp.c @@ -0,0 +1,267 @@ +/************************************************************************** +* +* Copyright (C) 2006 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "bacdef.h" +#include "mstp.h" +#include "dlmstp.h" +#include "rs485.h" +#include "npdu.h" + +/* receive buffer */ +static DLMSTP_PACKET Receive_Buffer; +/* temp buffer for NPDU insertion */ +static uint8_t PDU_Buffer[MAX_MPDU]; +/* local MS/TP port data */ +static volatile struct mstp_port_struct_t MSTP_Port; + +void dlmstp_init(void) +{ + /* initialize buffer */ + Receive_Buffer.ready = false; + Receive_Buffer.pdu_len = 0; + /* initialize hardware */ + RS485_Initialize(); + MSTP_Init(&MSTP_Port, MSTP_Port.This_Station); +} + +void dlmstp_cleanup(void) +{ + /* nothing to do for static buffers */ +} + +/* returns number of bytes sent on success, zero on failure */ +int dlmstp_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 */ + int bytes_sent = 0; + uint8_t frame_type = 0; + uint8_t destination = 0; /* destination address */ + BACNET_ADDRESS src; + unsigned mtu_len = 0; + + if (MSTP_Port.TxReady == false) { + if (npdu_data->confirmed_message) + MSTP_Port.TxFrameType = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + else + MSTP_Port.TxFrameType = + FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + + /* load destination MAC address */ + if (dest && dest->mac_len == 1) { + destination = dest->mac[0]; + } else { +#if PRINT_ENABLED + fprintf(stderr, "mstp: invalid destination MAC address!\n"); +#endif + return -2; + } + /* header len */ + mtu_len = 8; + if ((mtu_len + pdu_len) > MAX_MPDU) { +#if PRINT_ENABLED + fprintf(stderr, "mstp: PDU is too big to send!\n"); +#endif + return -4; + } + memmove(&PDU_Buffer[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + bytes_sent = MSTP_Create_Frame( + (uint8_t *) & MSTP_Port.TxBuffer[0], + sizeof(MSTP_Port.TxBuffer), + MSTP_Port.TxFrameType, + destination, MSTP_Port.This_Station, &PDU_Buffer[0], mtu_len); + MSTP_Port.TxLength = bytes_sent; + MSTP_Port.TxReady = true; + } + + return bytes_sent; +} + +/* called about once a millisecond */ +void dlmstp_millisecond_timer(void) +{ + MSTP_Millisecond_Timer(&MSTP_Port); +} + +/* returns the number of octets in the PDU, or zero on failure */ +/* This function is expecting to be polled. */ +uint16_t dlmstp_receive(BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ + uint16_t pdu_len = 0; + + (void) timeout; + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Port.ReceivedValidFrame == false) && + (MSTP_Port.ReceivedInvalidFrame == false)) { + RS485_Check_UART_Data(&MSTP_Port); + MSTP_Receive_Frame_FSM(&MSTP_Port); + } + /* only do master state machine while rx is idle */ + if (MSTP_Port.receive_state == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM(&MSTP_Port)) { + }; + } + /* see if there is a packet available */ + if (Receive_Buffer.ready) { + memmove(src, &Receive_Buffer.address, + sizeof(Receive_Buffer.address)); + pdu_len = Receive_Buffer.pdu_len; + memmove(&pdu[0], &Receive_Buffer.pdu[0], max_pdu); + Receive_Buffer.ready = false; + } + + return pdu_len; +} + +void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +/* for the MS/TP state machine to use for putting received data */ +uint16_t dlmstp_put_receive(uint8_t src, /* source MS/TP address */ + uint8_t * pdu, /* PDU data */ + uint16_t pdu_len) +{ + if (Receive_Buffer.ready) { + /* FIXME: what to do when we miss a message? */ + pdu_len = 0; + } else if (pdu_len < sizeof(Receive_Buffer.pdu)) { + dlmstp_fill_bacnet_address(&Receive_Buffer.address, src); + Receive_Buffer.pdu_len = pdu_len; + memmove(Receive_Buffer.pdu, pdu, pdu_len); + Receive_Buffer.ready = true; + } else { + /* FIXME: message too large? */ + pdu_len = 0; + } + + return pdu_len; +} + +void dlmstp_set_my_address(uint8_t mac_address) +{ + /* FIXME: Master Nodes can only have address 1-127 */ + MSTP_Port.This_Station = mac_address; + + return; +} + +/* This parameter represents the value of the Max_Info_Frames property of */ +/* the node's Device object. The value of Max_Info_Frames specifies the */ +/* maximum number of information frames the node may send before it must */ +/* pass the token. Max_Info_Frames may have different values on different */ +/* nodes. This may be used to allocate more or less of the available link */ +/* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ +/* node, its value shall be 1. */ +void dlmstp_set_max_info_frames(unsigned max_info_frames) +{ + MSTP_Port.Nmax_info_frames = max_info_frames; + + return; +} + +unsigned dlmstp_max_info_frames(void) +{ + return MSTP_Port.Nmax_info_frames; +} + +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +void dlmstp_set_max_master(uint8_t max_master) +{ + MSTP_Port.Nmax_master = max_master; + + return; +} + +uint8_t dlmstp_max_master(void) +{ + return MSTP_Port.Nmax_master; +} + +void dlmstp_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = MSTP_Port.This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* len=0 denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/ethernet.c b/bacnet-stack-0-3-0/ports/rtos32/ethernet.c new file mode 100644 index 00000000..5b61f510 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/ethernet.c @@ -0,0 +1,337 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include /* for the standard bool type. */ +#include /* for the standard bool type. */ +#include +#include +#include +#include +#include +#include "ethernet.h" +#include "bacdcode.h" +#include "npdu.h" + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +static SOCKET Ethernet_Socket = -1; +/* used for binding 802.2 */ +static struct sockaddr Ethernet_Address = { 0 }; + +bool ethernet_valid(void) +{ + return (Ethernet_Socket != -1); +} + +void ethernet_cleanup(void) +{ + if (ethernet_valid()) + closesocket(Ethernet_Socket); + Ethernet_Socket = -1; + + return; +} + +bool ethernet_init(char *interface_name) +{ + int value = 1; + + (void) interface_name; + /* setup the socket */ + Ethernet_Socket = socket(AF_INET, SOCK_RAW, 0); + /*Ethernet_Socket = socket(AF_INET, SOCK_STREAM, 0); */ + if (Ethernet_Socket < 0) + fprintf(stderr, "ethernet: failed to bind to socket!\r\n"); + Ethernet_Address.sa_family = AF_INET; + memset(Ethernet_Address.sa_data, 0, sizeof(Ethernet_Address.sa_data)); + if (bind(Ethernet_Socket, + &Ethernet_Address, sizeof(Ethernet_Address)) == SOCKET_ERROR) + fprintf(stderr, "ethernet: failed to bind to socket!\r\n"); + /*setsockopt(Ethernet_Socket,SOL_SOCKET,SO_802_2,(char *)&value,sizeof(value)); */ + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent on success, negative number on failure */ +int ethernet_send(BACNET_ADDRESS * dest, /* destination address */ + BACNET_ADDRESS * src, /* source 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 */ + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + (void) npdu_data; + /* don't waste time if the socket is not valid */ + if (Ethernet_Socket < 0) { + fprintf(stderr, "ethernet: 802.2 socket is invalid!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = dest->mac[i]; + mtu_len++; + } + } else { + fprintf(stderr, "ethernet: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = src->mac[i]; + mtu_len++; + } + } else { + fprintf(stderr, "ethernet: invalid source MAC address!\n"); + return -3; + } + if ((14 + 3 + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + /* packet length */ + mtu_len += encode_unsigned16(&mtu[12], + 3 /*DSAP,SSAP,LLC */ + pdu_len); + /* Logical PDU portion */ + mtu[mtu_len++] = 0x82; /* DSAP for BACnet */ + mtu[mtu_len++] = 0x82; /* SSAP for BACnet */ + mtu[mtu_len++] = 0x03; /* Control byte in header */ + mtu_len = 17; + if ((mtu_len + pdu_len) > MAX_MPDU) { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return -4; + } + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + /* packet length - only the logical portion, not the address */ + encode_unsigned16(&mtu[12], 3 + pdu_len); + + /* Send the packet */ + bytes = send(Ethernet_Socket, (const char *) &mtu, mtu_len, 0); + /* did it get sent? */ + if (bytes < 0) + fprintf(stderr, "ethernet: Error sending packet: %s\n", + strerror(errno)); + + return bytes; +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent on success, negative number on failure */ +int ethernet_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 */ + int i = 0; /* counter */ + BACNET_ADDRESS src = { 0 }; /* source address */ + + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + + /* FIXME: npdu_data? */ + /* function to send a packet out the 802.2 socket */ + /* returns 1 on success, 0 on failure */ + return ethernet_send(dest, /* destination address */ + &src, /* source address */ + npdu_data, pdu, /* any data to be sent - may be null */ + pdu_len); /* number of bytes of data */ +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_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; + + /* Make sure the socket is open */ + if (Ethernet_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(Ethernet_Socket, &read_fds); + max = Ethernet_Socket; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = + recv(Ethernet_Socket, (char *) &buf[0], MAX_MPDU, 0); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + /* EAGAIN Non-blocking I/O has been selected */ + /* using O_NONBLOCK and no data */ + /* was immediately available for reading. */ + if (errno != EAGAIN) + fprintf(stderr, + "ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + /*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) { + /*fprintf(stderr, "ethernet: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&buf[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &buf[17], pdu_len); + /* ignore packets that are too large */ + /* client should check my max apdu first */ + else + pdu_len = 0; + + return pdu_len; +} + +void ethernet_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_set_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + + if (info) + fprintf(stderr, "%s", info); + if (dest) { + fprintf(stderr, "Address:\n"); + fprintf(stderr, " MAC Length=%d\n", dest->mac_len); + fprintf(stderr, " MAC Address="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->mac[i]); + } + fprintf(stderr, "\n"); + fprintf(stderr, " Net=%hu\n", dest->net); + fprintf(stderr, " Len=%d\n", dest->len); + fprintf(stderr, " Adr="); + for (i = 0; i < MAX_MAC_LEN; i++) { + fprintf(stderr, "%02X ", (unsigned) dest->adr[i]); + } + fprintf(stderr, "\n"); + } + + return; +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/hardware.cfg b/bacnet-stack-0-3-0/ports/rtos32/hardware.cfg new file mode 100644 index 00000000..f85c1eb9 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/hardware.cfg @@ -0,0 +1,24 @@ +// * The target computer is IBM-PC-AT compatible. +// * There is a minimum of 4 MB of RAM installed. + +#ifdef DEBUGDOS + Region RealModeVectors 0 1k RAM NoAccess // interrupt vectors + Region BIOSDataArea 1k 3k RAM ReadOnly // BIOS data area + Region DOSMem 4k 252k RAM + Region LowMem 256k 256k RAM +#else + Region = RealModeVectors 0, 4k, RAM, NoAccess // interrupt vectors +// note: locate only has 4k granularity so the 0-1k will be readonly +// Region = RealModeVectors 0, 1k, RAM, NoAccess // interrupt vectors +// Region = BIOSDataArea 1k, 3k, RAM, ReadOnly // BIOS data area + Region = LowMem 4k, 508k, RAM, Assign // Conventional memory +#endif +Region = MoreLowMem 512k, 128k, RAM, Assign // Reserved BIOS ext +Region = MonoText B0000h 4k, Device, ReadWrite // Mono text video mem +Region = ColorText B8000h, 4k, Device, ReadWrite // Text mode video ram +Region = DiskOnChip D0000h, 8k, Device, ReadWrite // Driver Ampro Card +Region = DiskOnChip1 E8000h, 32k, Device, ReadWrite // Driver WinSys Card +Region = HighMem 1M, 3M, RAM, Assign // 3mb ext mem on target +Virtual HeapMem 1G +Virtual StackMem 2G +Virtual ProgMem 3G diff --git a/bacnet-stack-0-3-0/ports/rtos32/init.c b/bacnet-stack-0-3-0/ports/rtos32/init.c new file mode 100644 index 00000000..e4da3c1b --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/init.c @@ -0,0 +1,124 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 +#include +#include + +extern void RTEmuInit(void); + +#ifdef _MSC_VER +#define VOIDEXPORT _declspec(dllexport) void __cdecl +#else +#define VOIDEXPORT void __export __cdecl +#endif + +/* DISK SYSTEM */ +#ifdef DOC /* include DiskOnChip driver */ +#include +#define RTF_MAX_FILES 16 /* support for more open files (default is 8) */ +#define RTF_BUFFERS_IN_BSS /* we do not need file I/O before the run-time */ +#include /* system is initialized */ + + /*#define READ_HEAD_BUFFER_SIZE 2048+4 */ + + /*static BYTE ReadAheadBuffer[READ_HEAD_BUFFER_SIZE]; */ + +static RTFDrvFLPYData FLPYDriveAData = { 0 }; +static RTFDrvDOCData DOCDriveData = { 0 }; +static RTFDrvIDEData IDEDriveData = { 0 }; + +RTFDevice RTFDeviceList[] = { + /* type,number,flags,driver,driverdata */ + {RTF_DEVICE_FLOPPY, 0, 0, &RTFDrvFloppy, &FLPYDriveAData}, + {RTF_DEVICE_FDISK, 0, 0, &RTFDrvDOC, &DOCDriveData}, + {RTF_DEVICE_FDISK, 0, 0, &RTFDrvIDE, &IDEDriveData}, + {0, 0, 0, NULL, NULL} +}; +#endif +/* END OF DISK SYSTEM */ + +/* RTTarget only defines 64 Win32 handles, which are not enough for BACstac */ +/* the following code is from the RTTarget manual, page 106 */ +#define MAXHANDLES 1024 +#define MAXOBJECTS 1024 +#define MAXTYPES 32 + +RTW32Handle RTHandleTable[MAXHANDLES] = { {0} }; +int RTHandleCount = MAXHANDLES; + +RTW32Object RTObjectTable[MAXOBJECTS] = { {0} }; +int RTObjectCount = MAXOBJECTS; + +RTW32Types RTTypeTable[MAXTYPES] = { {0} }; +int RTTypeCount = MAXTYPES; + +#if 0 +/* We can embed some files in the RTB file, like a binary + file used for configuring a remote device, using + 'Locate File filename HighMem' in the config (.CFG) file. + However, the default setup for RTFiles and RTTarget + doesn't include the RAM files, so we need to specify + that here, as well as the LPT, console, and FAT. + From RTFiles-32 manual, ch. 7, "Using RTFiles-32 with + RTTarget-32" */ +RTFileSystem Console = { RT_FS_CONSOLE, 0, 0, &RTConsoleFileSystem }; + +RTFileSystem LPTFiles = { RT_FS_LPT_DEVICE, 0, 0, &RTLPTFileSystem }; + +/* logical drive Z: can be used to access the RAM drive */ +RTFileSystem RAMFiles = + { RT_FS_FILE, 1 << ('Z' - 'A'), 0, &RTRAMFileSystem }; + +/* logical drive A: through D: are reserved for FAT */ +RTFileSystem FATFiles = + { RT_FS_FILE | RT_FS_IS_DEFAULT, 0x0F, 0x03, &RTFilesFileSystem }; + +RTFileSystem *RTFileSystemList[] = { + &Console, + &LPTFiles, + &RAMFiles, + &FATFiles, + NULL, +}; +#endif + +/*-----------------------------------*/ +VOIDEXPORT Init(void) +{ + (void) RTSetFlags(RT_MM_VIRTUAL, 1); /* this is the better method */ + (void) RTCMOSExtendHeap(); /* get as much memory as we can */ + RTCMOSSetSystemTime(); /* get the right date and time */ + RTEmuInit(); /* set up floating point emulation */ + /* pizza - RTHaltCPL3 appears to cause problems with file handling */ + /*RTIdleHandler = (void RTTAPI *)RTHaltCPL3; // low power when idle */ + /* not needed with pre-emptive */ + /*RTKTimeSlice(2); // allow same priority task switch */ + RTKConfig.Flags |= RF_PREEMPTIVE; /* preemptive multitasking */ + RTKConfig.Flags |= RF_WIN32MUTEX_MUTEX; /* Win32 mutexes are RTK32 mutexes */ + RTKConfig.Flags |= RF_FPCONTEXT; /* saves floating point context for tasks */ + RTKConfig.HookedIRQs |= 1 << 1; /* hook the keyboard IRQ */ + RTKConfig.DefaultTaskStackSize = 1024 * 8; /* for Win32 task stacks req = 0 */ +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/main.c b/bacnet-stack-0-3-0/ports/rtos32/main.c new file mode 100644 index 00000000..dfe95ad0 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/main.c @@ -0,0 +1,172 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + +/* This is one way to use the embedded BACnet stack under RTOS-32 */ +/* compiled with Borland C++ 5.02 */ +#include +#include +#include +#include /* for kbhit */ +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "handlers.h" +#include "datalink.h" +#include "iam.h" +#include "txbuf.h" + +/* RTOS-32 */ +#include "rtkernel.h" +#if defined(RTK32_VER) +#define _USER32_ +#define _KERNEL32_ +#include +#include /* for RTCMOSSetSystemTime */ +#include /* file system */ +#include /* file system */ +#include +#endif +#include /* serial port driver */ +#include /* time measurement & timer interrupt rate control */ +#include /* interrupt handler for the keyboard */ + +/* buffers used for transmit and receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void Init_Service_Handlers(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + /* 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); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); +} + +void millisecond_task(void) +{ + Time ticks = 0; /* task cycle */ + int i = 0; /* loop counter */ + + ticks = RTKGetTime() + MilliSecsToTicks(1); + while (TRUE) { + RTKDelayUntil(ticks); + dlmstp_millisecond_timer(); + ticks += MilliSecsToTicks(1); + } +} + +void RTOS_Initialize(void) +{ + /* allow OS to setup IRQ 1 by using a dummy call */ + (void) kbhit(); + RTKernelInit(5); /* get the kernel going */ + RTKeybrdInit(); + /*(void)CPUMoniInit(); /* not needed - just monitor idle task */ */ + RTComInit(); + ITimerInit(); + + if (RTCallDebugger(RT_DBG_MONITOR, 0, 0) != -1) { + /* Win32 structured exception - if no handler is + installed, TerminateProcess() will be called, + which will reboot - a good thing in our case. */ + RTRaiseCPUException(0); /* Divide Error DIV and IDIV instructions. */ + RTRaiseCPUException(1); /* Debug Any code or data reference. */ + RTRaiseCPUException(2); /* NMI */ + RTRaiseCPUException(3); /* Breakpoint INT 3 instruction. */ + RTRaiseCPUException(4); /* Overflow INTO instruction. */ + RTRaiseCPUException(5); /* BOUND Range Exceeded BOUND instruction. */ + RTRaiseCPUException(6); /* Invalid Opcode (Undefined Opcode) */ + /* RTRaiseCPUException(7); // Device Not Available (No Math Coprocessor) */ + RTRaiseCPUException(8); /* Double Fault any exception instruction,NMI,INTR. */ + RTRaiseCPUException(9); /* Co-Processor overrun */ + RTRaiseCPUException(10); /* Invalid TSS Task switch or TSS access. */ + RTRaiseCPUException(11); /* Segment Not Present Loading segment registers */ + RTRaiseCPUException(12); /* Stack Seg Fault Stack ops /SS reg loads. */ + RTRaiseCPUException(13); /* General Protection Any memory reference */ + RTRaiseCPUException(14); /* Page Fault Any memory reference. */ + RTRaiseCPUException(15); /* reserved */ + RTRaiseCPUException(16); /* Floating-Point Error (Math Fault) */ + } + /* setup 1ms timer tick */ + SetTimerIntVal(1000); + /* per recommendation in manual */ + RTKDelay(1); + RTCMOSSetSystemTime(); /* get the right time-of-day */ + + /* create timer tick task */ + RTKCreateTask(millisecond_task, 16, 1024 * 8, "millisec task"); +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + + (void) argc; + (void) argv; + Device_Set_Object_Instance_Number(126); + Init_Service_Handlers(); + RTOS_Initialize(); + /* init the physical layer */ +#ifdef BACDL_BIP + if (!bip_init()) + return 1; +#endif +#ifdef BACDL_ETHERNET + if (!ethernet_init(NULL)) + return 1; +#endif +#ifdef BACDL_MSTP + dlmstp_set_my_address(0x05); + dlmstp_init(); +#endif + iam_send(&Handler_Transmit_Buffer[0]); + /* loop forever */ + for (;;) { + /* input */ + + /* 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); + } + /* output */ + + + + /* blink LEDs, Turn on or off outputs, etc */ + } +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/makefile.mak b/bacnet-stack-0-3-0/ports/rtos32/makefile.mak new file mode 100644 index 00000000..505fda05 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/makefile.mak @@ -0,0 +1,197 @@ +# +# Simple makefile to build an RTB executable for RTOS-32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef RTOS32_DIR +RTOS32_DIR_Not_Defined: + @echo . + @echo You must define environment variable RTOS32_DIR to compile. +!endif + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile. +!endif + +PRODUCT = bacnet +PRODUCT_RTB = $(PRODUCT).rtb +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_BIP=1 +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ETHERNET=1 +#DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=1;BACDL_ARCNET=1 +DEFINES = -DDOC;BIG_ENDIAN=0;TSM_ENABLED=1;PRINT_ENABLED=0;BACDL_MSTP=1 + +SRCS = main.c \ + ethernet.c \ + bip-init.c \ + dlmstp.c \ + rs485.c \ + init.c \ + ..\..\bip.c \ + ..\..\mstp.c \ + ..\..\crc.c \ + ..\..\demo\handler\h_iam.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_wp.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\s_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\bacapp.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\dcc.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.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\lsp.c \ + ..\..\demo\object\mso.c \ + ..\..\datalink.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 +LOCATE = $(RTOS32_DIR)\bin\rtloc + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +INCL_DIRS = -I$(BORLAND_DIR)\include;$(RTOS32_DIR)\include;..\..\;..\..\demo\handler\;..\..\demo\object\;. + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +RTOS32_LIB_DIR = $(RTOS32_DIR)\libbc +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBDIR = $(RTOS32_LIB_DIR);$(C_LIB_DIR) + +LIBS = $(RTOS32_LIB_DIR)\RTFILES.LIB \ +$(RTOS32_LIB_DIR)\RTFSK32.LIB \ +$(RTOS32_LIB_DIR)\DRVDOC.LIB \ +$(RTOS32_LIB_DIR)\RTIP.LIB \ +$(RTOS32_LIB_DIR)\RTK32.LIB \ +$(RTOS32_LIB_DIR)\FLTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\DRVRT32.LIB \ +$(RTOS32_LIB_DIR)\RTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\RTT32.LIB \ +$(RTOS32_LIB_DIR)\RTTHEAP.LIB \ +#$(C_LIB_DIR)\DPMI32.lib \ +$(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : $(PRODUCT_RTB) monitor.rtb + +monitor.rtb: monitor.cfg hardware.cfg + $(LOCATE) monitor + +# debug using COM3 (ISA Card) as the debug port +# boot from floppy +debugcom3: hardware.cfg software.cfg $(PRODUCT_RTB) monitor.rtb + $(LOCATE) -DDEBUGCOM3 monitor + $(LOCATE) -d- -DMONITOR -DDEBUGCOM3 $(PRODUCT) software.cfg + +$(PRODUCT_RTB): bcc32.cfg hardware.cfg software.cfg $(PRODUCT_EXE) + @echo Running Locate on $(PRODUCT) + $(LOCATE) $(PRODUCT) software.cfg + +# 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$(LINKER_LIB) -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), $(PRODUCT_RTB) and map files. + del *.obj + del ..\..\*.obj + del $(PRODUCT_EXE) + del $(PRODUCT_RTB) + del *.map + del bcc32.cfg + +install : $(PRODUCT) + copy $(PRODUCT) ..\bin + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +bcc32.cfg : + Copy &&| +$(CFLAGS) +-c +#-g2 #stop after gN warnings +-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 diff --git a/bacnet-stack-0-3-0/ports/rtos32/monitor.cfg b/bacnet-stack-0-3-0/ports/rtos32/monitor.cfg new file mode 100644 index 00000000..68417102 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/monitor.cfg @@ -0,0 +1,47 @@ +// Configuration files for the RTTarget-32 Debug Monitor and Borland C/C++. + +// Some general parameters for this file are: +// +// * The default disk boot code is used to boot the system from +// a floppy disk, hard disk, or ROM disk. +// * Pageing is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The target PC is assumed to have a color display. +// * The target PC uses COM1 to communicated with the host. +// * 115200 baud is used for host - target communication. + + +@HARDWARE.CFG // pull in hardware definitions + +Locate BootCode BIOSBOOT.EXE LowMem // boot from disk +Locate BootData BootData LowMem 0 16 // boot stuff must be in conventional memory +Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code +CPL = 0 + +Locate Section CODE LowMem 1 // Monitor's code section +Locate Header Monitor LowMem 0 4 // and header +Locate Section DATA LowMem 2 // data section +Locate Stack Stack LowMem 1k 4 // and a small stack, no heap +Locate PageTable Pages LowMem + +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy CODE LowMem // compress everything +Locate Copy DATA LowMem // ditto +Locate Copy Pages LowMem // ditto + +#ifdef DEBUGCOM1 +COMPort COM1 115200 // use COM1 with 115200 baud +VideoRAM = None // program output sent to debugger - clrscr() crashes it. +#elifdef DEBUGCOM3 +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +//VideoRAM = ColorText // program output sent to Graphic Card +VideoRAM = None // program output sent to debugger - clrscr() crashes it. +#else +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +VideoRAM = ColorText // program output sent to Graphic Card +#endif + +IgnoreMsg "No heap" // the monitor does not need a heap diff --git a/bacnet-stack-0-3-0/ports/rtos32/mstp.c b/bacnet-stack-0-3-0/ports/rtos32/mstp.c new file mode 100644 index 00000000..5303eabf --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/mstp.c @@ -0,0 +1,1749 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2003 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####*/ + +/* This clause describes a Master-Slave/Token-Passing (MS/TP) data link */ +/* protocol, which provides the same services to the network layer as */ +/* ISO 8802-2 Logical Link Control. It uses services provided by the */ +/* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be */ +/* included in this standard by reference. The following hardware is assumed: */ +/* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of */ +/* transmitting and receiving eight data bits with one stop bit */ +/* and no parity. */ +/* (b) An EIA-485 transceiver whose driver may be disabled. */ +/* (c) A timer with a resolution of five milliseconds or less */ + +#include +#include +#if PRINT_ENABLED +#include +#endif +#include "mstp.h" +#include "bytes.h" +#include "crc.h" +#include "rs485.h" + +/* debug print statements */ +#if PRINT_ENABLED +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 1 +#define PRINT_ENABLED_MASTER 0 +#else +#define PRINT_ENABLED_RECEIVE 0 +#define PRINT_ENABLED_RECEIVE_DATA 0 +#define PRINT_ENABLED_MASTER 0 +#endif + +/* MS/TP Frame Format */ +/* All frames are of the following format: */ +/* */ +/* Preamble: two octet preamble: X`55', X`FF' */ +/* Frame Type: one octet */ +/* Destination Address: one octet address */ +/* Source Address: one octet address */ +/* Length: two octets, most significant octet first, of the Data field */ +/* Header CRC: one octet */ +/* Data: (present only if Length is non-zero) */ +/* Data CRC: (present only if Length is non-zero) two octets, */ +/* least significant octet first */ +/* (pad): (optional) at most one octet of padding: X'FF' */ + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +const unsigned Npoll = 50; + +/* The number of retries on sending Token: 1. */ +const unsigned Nretry_token = 1; + +/* The minimum number of DataAvailable or ReceiveError events that must be */ +/* seen by a receiving node in order to declare the line "active": 4. */ +const uint8_t Nmin_octets = 4; + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +const uint16_t Tframe_abort = 30; + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +const unsigned Tframe_gap = 20; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +const uint16_t Tno_token = 500; + +/* The maximum time after the end of the stop bit of the final */ +/* octet of a transmitted frame before a node must disable its */ +/* EIA-485 driver: 15 bit times. */ +const unsigned Tpostdrive = 15; + +/* The maximum time a node may wait after reception of a frame that expects */ +/* a reply before sending the first octet of a reply or Reply Postponed */ +/* frame: 250 milliseconds. */ +const uint16_t Treply_delay = 10; + +/* The minimum time without a DataAvailable or ReceiveError event */ +/* that a node must wait for a station to begin replying to a */ +/* confirmed request: 255 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 300 milliseconds.) */ +const uint16_t Treply_timeout = 255; + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +const unsigned Troff = 30; + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +const uint16_t Tslot = 10; + +/* The maximum time a node may wait after reception of the token or */ +/* a Poll For Master frame before sending the first octet of a frame: */ +/* 15 milliseconds. */ +const uint16_t Tusage_delay = 15; + +/* The minimum time without a DataAvailable or ReceiveError event that a */ +/* node must wait for a remote node to begin using a token or replying to */ +/* a Poll For Master frame: 20 milliseconds. (Implementations may use */ +/* larger values for this timeout, not to exceed 100 milliseconds.) */ +const uint16_t Tusage_timeout = 30; + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} + + +bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port) +{ + return (mstp_port->EventCount > Nmin_octets); +} + +unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + unsigned index = 0; /* used to load the data portion of the frame */ + + /* not enough to do a header */ + if (buffer_len < 8) + return 0; + + buffer[0] = 0x55; + buffer[1] = 0xFF; + buffer[2] = frame_type; + crc8 = CRC_Calc_Header(buffer[2], crc8); + buffer[3] = destination; + crc8 = CRC_Calc_Header(buffer[3], crc8); + buffer[4] = source; + crc8 = CRC_Calc_Header(buffer[4], crc8); + buffer[5] = data_len / 256; + crc8 = CRC_Calc_Header(buffer[5], crc8); + buffer[6] = data_len % 256; + crc8 = CRC_Calc_Header(buffer[6], crc8); + buffer[7] = ~crc8; + + index = 8; + while (data_len && data && (index < buffer_len)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + /* append the data CRC if necessary */ + if (index > 8) { + if ((index + 2) <= buffer_len) { + crc16 = ~crc16; + buffer[index] = LO_BYTE(crc16); + index++; + buffer[index] = HI_BYTE(crc16); + index++; + } else + return 0; + } + + return index; /* returns the frame length */ +} + +void MSTP_Create_And_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t buffer[MAX_MPDU] = { 0 }; /* buffer for sending */ + uint16_t len = 0; /* number of bytes to send */ + + len = (uint16_t) MSTP_Create_Frame(&buffer[0], /* where frame is loaded */ + sizeof(buffer), /* amount of space available */ + frame_type, /* type of frame to send - see defines */ + destination, /* destination address */ + source, /* source address */ + data, /* any data to be sent - may be null */ + data_len); /* number of bytes of data (up to 501) */ + + RS485_Send_Frame(mstp_port, &buffer[0], len); + /* FIXME: be sure to reset SilenceTimer after each octet is sent! */ +} + +/* Millisecond Timer - called every millisecond */ +void MSTP_Millisecond_Timer(volatile struct mstp_port_struct_t *mstp_port) +{ + INCREMENT_AND_LIMIT_UINT16(mstp_port->SilenceTimer); + INCREMENT_AND_LIMIT_UINT16(mstp_port->ReplyPostponedTimer); + return; +} + +#if PRINT_ENABLED_RECEIVE +char *mstp_receive_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_RECEIVE_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + text = "PREAMBLE"; + break; + case MSTP_RECEIVE_STATE_HEADER: + text = "HEADER"; + break; + case MSTP_RECEIVE_STATE_HEADER_CRC: + text = "HEADER_CRC"; + break; + case MSTP_RECEIVE_STATE_DATA: + text = "DATA"; + break; + default: + break; + } + + return text; +} +#endif + +void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) +{ +#if PRINT_ENABLED_RECEIVE_DATA + static MSTP_RECEIVE_STATE receive_state = MSTP_RECEIVE_STATE_IDLE; +#endif +#if PRINT_ENABLED_RECEIVE + fprintf(stderr, + "MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u Silence=%u\n", + mstp_receive_state_text(mstp_port->receive_state), + mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index, + mstp_port->EventCount, mstp_port->DataLength, + mstp_port->SilenceTimer); +#endif + switch (mstp_port->receive_state) { + /* In the IDLE state, the node waits for the beginning of a frame. */ + case MSTP_RECEIVE_STATE_IDLE: + /* EatAnError */ + if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "MSTP Rx: %02X ", mstp_port->DataRegister); +#endif + /* Preamble1 */ + if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* EatAnOctet */ + else { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "\n"); +#endif + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ + case MSTP_RECEIVE_STATE_PREAMBLE: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* Preamble2 */ + if (mstp_port->DataRegister == 0xFF) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->Index = 0; + mstp_port->HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* ignore RepeatedPreamble1 */ + else if (mstp_port->DataRegister == 0x55) { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the second preamble octet. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; + } + /* NotPreamble */ + else { + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER state, the node waits for the fixed message header. */ + case MSTP_RECEIVE_STATE_HEADER: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* FrameType */ + if (mstp_port->Index == 0) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->FrameType = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 1; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Destination */ + else if (mstp_port->Index == 1) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DestinationAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 2; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Source */ + else if (mstp_port->Index == 2) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->SourceAddress = mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 3; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length1 */ + else if (mstp_port->Index == 3) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength = mstp_port->DataRegister * 256; + mstp_port->DataAvailable = false; + mstp_port->Index = 4; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* Length2 */ + else if (mstp_port->Index == 4) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataLength += mstp_port->DataRegister; + mstp_port->DataAvailable = false; + mstp_port->Index = 5; + mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER; + } + /* HeaderCRC */ + else if (mstp_port->Index == 5) { + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + mstp_port->HeaderCRC = + CRC_Calc_Header(mstp_port->DataRegister, + mstp_port->HeaderCRC); + mstp_port->DataAvailable = false; + /* don't wait for next state - do it here */ + /* MSTP_RECEIVE_STATE_HEADER_CRC */ + if (mstp_port->HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else { + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) + || (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* FrameTooLong */ + if (mstp_port->DataLength > MAX_MPDU) { + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* NoData */ + else if (mstp_port->DataLength == 0) { + /* CHEAT: it is very difficult to respond to + poll for master in the Master Node state machine + before Tusage_timeout, so we will do it here. */ + if ((mstp_port->FrameType == + FRAME_TYPE_POLL_FOR_MASTER) + && (mstp_port->DestinationAddress == + mstp_port->This_Station) + && (mstp_port->master_state == + MSTP_MASTER_STATE_IDLE)) { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + /* don't indicate that a frame has been received */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + } else { + /* indicate that a frame with no data has been received */ + mstp_port->ReceivedValidFrame = true; + } + /* wait for the start of the next frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_IDLE; + } + /* Data */ + else { + mstp_port->Index = 0; + mstp_port->DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + mstp_port->receive_state = + MSTP_RECEIVE_STATE_DATA; + } + } + /* NotForUs */ + else { + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + + + } + /* not per MS/TP standard, but it is a case not covered */ + else { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); + /* indicate that an error has occurred during */ + /* the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + /* In the HEADER_CRC state, the node validates the CRC on the fixed */ + /* message header. */ + case MSTP_RECEIVE_STATE_HEADER_CRC: + break; + /* In the DATA state, the node waits for the data portion of a frame. */ + case MSTP_RECEIVE_STATE_DATA: + /* Timeout */ + if (mstp_port->SilenceTimer > Tframe_abort) { + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + /* Error */ + else if (mstp_port->ReceiveError == true) { + mstp_port->ReceiveError = false; + mstp_port->SilenceTimer = 0; + /* indicate that an error has occurred during the reception of a frame */ + mstp_port->ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } else if (mstp_port->DataAvailable == true) { +#if PRINT_ENABLED_RECEIVE_DATA + fprintf(stderr, "%02X ", mstp_port->DataRegister); +#endif + /* DataOctet */ + if (mstp_port->Index < mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC1 */ + else if (mstp_port->Index == mstp_port->DataLength) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + mstp_port->Index++; + mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA; + } + /* CRC2 */ + else if (mstp_port->Index == (mstp_port->DataLength + 1)) { + mstp_port->DataCRC = CRC_Calc_Data(mstp_port->DataRegister, + mstp_port->DataCRC); + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ + if (mstp_port->DataCRC == 0xF0B8) + mstp_port->ReceivedValidFrame = true; + else + mstp_port->ReceivedInvalidFrame = true; + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + } + mstp_port->DataAvailable = false; + mstp_port->SilenceTimer = 0; + } + break; + default: + /* shouldn't get here - but if we do... */ + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + break; + } +#if PRINT_ENABLED_RECEIVE_DATA + if ((receive_state != MSTP_RECEIVE_STATE_IDLE) && + (mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) { + fprintf(stderr, "\n"); + fflush(stderr); + } + receive_state = mstp_port->receive_state; +#endif + + return; +} + +#if PRINT_ENABLED +char *mstp_master_state_text(int state) +{ + char *text = "unknown"; + + switch (state) { + case MSTP_MASTER_STATE_INITIALIZE: + text = "INITIALIZE"; + break; + case MSTP_MASTER_STATE_IDLE: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_USE_TOKEN: + text = "USE_TOKEN"; + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + text = "WAIT_FOR_REPLY"; + break; + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + text = "IDLE"; + break; + case MSTP_MASTER_STATE_PASS_TOKEN: + text = "DONE_WITH_TOKEN"; + break; + case MSTP_MASTER_STATE_NO_TOKEN: + text = "NO_TOKEN"; + break; + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + text = "ANSWER_DATA_REQUEST"; + break; + default: + break; + } + + return text; +} +#endif + +#if PRINT_ENABLED +char *mstp_frame_type_text(int type) +{ + char *text = "unknown"; + + switch (type) { + case FRAME_TYPE_TOKEN: + text = "TOKEN"; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + text = "POLL_FOR_MASTER"; + break; + case FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER: + text = "REPLY_TO_POLL_FOR_MASTER"; + break; + case FRAME_TYPE_TEST_REQUEST: + text = "TEST_REQUEST"; + break; + case FRAME_TYPE_TEST_RESPONSE: + text = "TEST_RESPONSE"; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + text = "BACNET_DATA_EXPECTING_REPLY"; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + text = "BACNET_DATA_NOT_EXPECTING_REPLY"; + break; + case FRAME_TYPE_REPLY_POSTPONED: + text = "REPLY_POSTPONED"; + break; + default: + if ((type >= FRAME_TYPE_PROPRIETARY_MIN) && + (type <= FRAME_TYPE_PROPRIETARY_MAX)) + text = "PROPRIETARY"; + break; + } + + return text; +} +#endif + +/* returns true if we need to transition immediately */ +bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) +{ + int mtu_len = 0; + int frame_type = 0; + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + uint16_t my_timeout = 10, ns_timeout = 0; + /* transition immediately to the next state */ + bool transition_now = false; +#if PRINT_ENABLED_MASTER + static MSTP_MASTER_STATE master_state = MSTP_MASTER_STATE_INITIALIZE; +#endif + + /* some calculations that several states need */ + next_poll_station = (mstp_port->Poll_Station + 1) % + (mstp_port->Nmax_master + 1); + next_this_station = (mstp_port->This_Station + 1) % + (mstp_port->Nmax_master + 1); + next_next_station = (mstp_port->Next_Station + 1) % + (mstp_port->Nmax_master + 1); +#if PRINT_ENABLED_MASTER + if (mstp_port->master_state != master_state) { + master_state = mstp_port->master_state; + fprintf(stderr, + "MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u %s\n", + mstp_port->This_Station, + next_this_station, + mstp_port->Next_Station, + next_next_station, + mstp_port->Poll_Station, + next_poll_station, + mstp_port->EventCount, + mstp_port->TokenCount, + mstp_port->SilenceTimer, + mstp_master_state_text(mstp_port->master_state)); + } +#endif + + switch (mstp_port->master_state) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->Poll_Station = mstp_port->This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + mstp_port->TokenCount = Npoll; + mstp_port->SoleMaster = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + /* In the IDLE state, the node waits for a frame. */ + case MSTP_MASTER_STATE_IDLE: + /* LostToken */ + if (mstp_port->SilenceTimer >= Tno_token) { + /* assume that the token has been lost */ + mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */ + mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } + /* ReceivedInvalidFrame */ + else if (mstp_port->ReceivedInvalidFrame == true) { + /* invalid frame was received */ + mstp_port->ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (mstp_port->ReceivedValidFrame == true) { +#if PRINT_ENABLED_MASTER + fprintf(stderr, + "MSTP: ReceivedValidFrame Src=%02X Dest=%02X DataLen=%u FC=%u ST=%u Type=%s\n", + mstp_port->SourceAddress, + mstp_port->DestinationAddress, + mstp_port->DataLength, + mstp_port->FrameCount, + mstp_port->SilenceTimer, + mstp_frame_type_text(mstp_port->FrameType)); +#endif + /* destined for me! */ + if ((mstp_port->DestinationAddress == + mstp_port->This_Station) || + (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + switch (mstp_port->FrameType) { + /* ReceivedToken */ + case FRAME_TYPE_TOKEN: + /* tokens can't be broadcast */ + if (mstp_port->DestinationAddress == + MSTP_BROADCAST_ADDRESS) + break; + mstp_port->ReceivedValidFrame = false; + mstp_port->FrameCount = 0; + mstp_port->SoleMaster = false; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + /* ReceivedPFM */ + case FRAME_TYPE_POLL_FOR_MASTER: + /* CHEAT: we cheat a little and this is really handled in the + receive state machine since it is difficult to respond + quick enough (i.e. faster than Tusage_timeout of the + other node which could be 20ms). */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + mstp_port->ReplyPostponedTimer = 0; + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + /* broadcast DER just remains IDLE */ + if (mstp_port->DestinationAddress != + MSTP_BROADCAST_ADDRESS) { + mstp_port->master_state = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + transition_now = true; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, mstp_port->This_Station, + NULL, 0); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + } + mstp_port->ReceivedValidFrame = false; + } + break; + /* In the USE_TOKEN state, the node is allowed to send one or */ + /* more data frames. These may be BACnet Data frames or */ + /* proprietary frames. */ + case MSTP_MASTER_STATE_USE_TOKEN: + if (!mstp_port->TxReady) { + /* NothingToSend */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + } else { + uint8_t destination = mstp_port->TxBuffer[3]; + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], mstp_port->TxLength); + mstp_port->FrameCount++; + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (destination == MSTP_BROADCAST_ADDRESS) + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + mstp_port->master_state = + MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + mstp_port->TxReady = false; + } + transition_now = true; + break; + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + if (mstp_port->SilenceTimer >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + mstp_port->FrameCount = mstp_port->Nmax_info_frames; + mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + /* Any retry of the data frame shall await the next entry */ + /* to the USE_TOKEN state. (Because of the length of the timeout, */ + /* this transition will cause the token to be passed regardless */ + /* of the initial value of FrameCount.) */ + transition_now = true; + } else { + if (mstp_port->ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + mstp_port->ReceivedInvalidFrame = false; + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (mstp_port->ReceivedValidFrame == true) { + if (mstp_port->DestinationAddress == + mstp_port->This_Station) { + switch (mstp_port->TxFrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + dlmstp_put_receive(mstp_port->SourceAddress, /* source MS/TP address */ + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens. */ + /* Synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } + } + break; + /* The DONE_WITH_TOKEN state either sends another data frame, */ + /* passes the token, or initiates a Poll For Master cycle. */ + case MSTP_MASTER_STATE_DONE_WITH_TOKEN: + /* SendAnotherFrame */ + if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (mstp_port->TokenCount < (Npoll - 1)) { + if ((mstp_port->SoleMaster == true) && + (mstp_port->Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + mstp_port->FrameCount = 0; + mstp_port->TokenCount++; + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* The comparison of NS and TS+1 eliminates the Poll For Master */ + /* if there are no addresses between TS and NS, since there is no */ + /* address at which a new master node may be found in that case. */ + mstp_port->TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == mstp_port->Next_Station) { + if (mstp_port->SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + mstp_port->Poll_Station = mstp_port->This_Station; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + mstp_port->EventCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + mstp_port->Poll_Station = next_poll_station; + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + transition_now = true; + break; + /* The PASS_TOKEN state listens for a successor to begin using */ + /* the token that this node has just attempted to pass. */ + case MSTP_MASTER_STATE_PASS_TOKEN: + if (mstp_port->SilenceTimer < Tusage_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (mstp_port->RetryCount < Nretry_token) { + /* RetrySendToken */ + mstp_port->RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + mstp_port->Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* no known successor node */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + transition_now = true; + } + } + break; + /* The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater */ + /* than Tno_token, indicating that there has been no network activity */ + /* for that period of time. The timeout is continued to determine */ + /* whether or not this node may create a token. */ + case MSTP_MASTER_STATE_NO_TOKEN: + my_timeout = Tno_token + (Tslot * mstp_port->This_Station); + if (mstp_port->SilenceTimer < my_timeout) { + if (mstp_port->EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = + Tno_token + (Tslot * (mstp_port->This_Station + 1)); + if (mstp_port->SilenceTimer < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + mstp_port->Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, mstp_port->This_Station, NULL, + 0); + /* indicate that the next station is unknown */ + mstp_port->Next_Station = mstp_port->This_Station; + mstp_port->RetryCount = 0; + mstp_port->TokenCount = 0; + /* mstp_port->EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + mstp_port->master_state = + MSTP_MASTER_STATE_POLL_FOR_MASTER; + transition_now = true; + } + } + break; + /* In the POLL_FOR_MASTER state, the node listens for a reply to */ + /* a previously sent Poll For Master frame in order to find */ + /* a successor node. */ + case MSTP_MASTER_STATE_POLL_FOR_MASTER: + if (mstp_port->ReceivedValidFrame == true) { + if ((mstp_port->DestinationAddress == mstp_port->This_Station) + && (mstp_port->FrameType == + FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + mstp_port->SoleMaster = false; + mstp_port->Next_Station = mstp_port->SourceAddress; + mstp_port->EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, NULL, + 0); + mstp_port->Poll_Station = mstp_port->This_Station; + mstp_port->TokenCount = 0; + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } + mstp_port->ReceivedValidFrame = false; + transition_now = true; + } else if ((mstp_port->SilenceTimer >= Tusage_timeout) || + (mstp_port->ReceivedInvalidFrame == true)) { + if (mstp_port->SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + mstp_port->FrameCount = 0; + /* mstp_port->TokenCount++; removed in 2004 */ + mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN; + } else { + if (mstp_port->Next_Station != mstp_port->This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + mstp_port->EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TOKEN, + mstp_port->Next_Station, mstp_port->This_Station, + NULL, 0); + mstp_port->RetryCount = 0; + mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != mstp_port->This_Station) { + /* SendNextPFM */ + mstp_port->Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_POLL_FOR_MASTER, + mstp_port->Poll_Station, + mstp_port->This_Station, NULL, 0); + mstp_port->RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + mstp_port->SoleMaster = true; + mstp_port->FrameCount = 0; + mstp_port->master_state = + MSTP_MASTER_STATE_USE_TOKEN; + } + } + } + mstp_port->ReceivedInvalidFrame = false; + transition_now = true; + } + break; + /* The ANSWER_DATA_REQUEST state is entered when a */ + /* BACnet Data Expecting Reply, a Test_Request, or */ + /* a proprietary frame that expects a reply is received. */ + case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST: + if (mstp_port->ReplyPostponedTimer <= Treply_delay) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + if ((mstp_port->FrameType == + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) + && (mstp_port->TxReady)) { + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->TxBuffer[0], + mstp_port->TxLength); + mstp_port->TxReady = false; + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + /* Test Request */ + /* If a receiving node can successfully receive and return */ + /* the information field, it shall do so. If it cannot receive */ + /* and return the entire information field but can detect */ + /* the reception of a valid Test_Request frame */ + /* (for example, by computing the CRC on octets as */ + /* they are received), then the receiving node shall discard */ + /* the information field and return a Test_Response containing */ + /* no information field. If the receiving node cannot detect */ + /* the valid reception of frames with overlength information fields, */ + /* then no response shall be returned. */ + else if (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST) { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_TEST_RESPONSE, + mstp_port->SourceAddress, + mstp_port->This_Station, + (uint8_t *) & mstp_port->InputBuffer[0], + mstp_port->DataLength); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + else { + MSTP_Create_And_Send_Frame(mstp_port, + FRAME_TYPE_REPLY_POSTPONED, + mstp_port->SourceAddress, + mstp_port->This_Station, NULL, 0); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + break; + default: + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port, + uint8_t this_station_mac) +{ + int i; /*loop counter */ + + if (mstp_port) { + mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + mstp_port->ReceiveError = false; + mstp_port->DataAvailable = false; + mstp_port->DataRegister = 0; + mstp_port->DataCRC = 0; + mstp_port->DataCRC = 0; + mstp_port->DataLength = 0; + mstp_port->DestinationAddress = 0; + mstp_port->EventCount = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->FrameCount = 0; + mstp_port->HeaderCRC = 0; + mstp_port->Index = 0; + mstp_port->Index = 0; + for (i = 0; i < sizeof(mstp_port->InputBuffer); i++) { + mstp_port->InputBuffer[i] = 0; + } + mstp_port->Next_Station = this_station_mac; + mstp_port->Poll_Station = this_station_mac; + mstp_port->ReceivedInvalidFrame = false; + mstp_port->ReceivedValidFrame = false; + mstp_port->RetryCount = 0; + mstp_port->SilenceTimer = 0; + mstp_port->ReplyPostponedTimer = 0; + mstp_port->SoleMaster = false; + mstp_port->SourceAddress = 0; + mstp_port->TokenCount = 0; + mstp_port->This_Station = this_station_mac; + mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES; + mstp_port->Nmax_master = DEFAULT_MAX_MASTER; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + for (i = 0; i < sizeof(mstp_port->TxBuffer); i++) { + mstp_port->TxBuffer[i] = 0; + } + mstp_port->TxLength = 0; + mstp_port->TxReady = false; + mstp_port->TxFrameType = 0; + + } +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* test stub functions */ +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + (void) mstp_port; + (void) buffer; + (void) nbytes; +} + +#define RING_BUFFER_DATA_SIZE 1 +#define RING_BUFFER_SIZE MAX_MPDU +static RING_BUFFER Test_Buffer; +static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE]; +static void Load_Input_Buffer(uint8_t * buffer, size_t len) +{ + static bool initialized = false; /* tracks our init */ + + + if (!initialized) { + initialized = true; + Ringbuf_Init(&Test_Buffer, + (char *) Test_Buffer_Data, + RING_BUFFER_DATA_SIZE, RING_BUFFER_SIZE); + } + /* empty any the existing data */ + while (!Ringbuf_Empty(&Test_Buffer)) { + (void) Ringbuf_Pop_Front(&Test_Buffer); + } + + if (buffer) { + while (len) { + (void) Ringbuf_Put(&Test_Buffer, (char *) buffer); + len--; + buffer++; + } + } +} + +void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + char *data; + if (!Ringbuf_Empty(&Test_Buffer) && mstp_port && + (mstp_port->DataAvailable == false)) { + data = Ringbuf_Pop_Front(&Test_Buffer); + if (data) { + mstp_port->DataRegister = *data; + mstp_port->DataAvailable = true; + } + } +} + +void testReceiveNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { 0 }; + uint8_t data[MAX_MPDU - 8 /*header */ - 2 /*CRC*/] = { 0 }; + + MSTP_Init(&mstp_port, my_mac); + + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + mstp_port.SilenceTimer = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the timeout */ + mstp_port.SilenceTimer = Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceiveError == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 0); + ct_test(pTest, mstp_port.HeaderCRC == 0xFF); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 1); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, FrameType == FRAME_TYPE_TOKEN); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 2); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DestinationAddress == 0x10); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 3); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.SourceAddress == my_mac); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 4); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER); + ct_test(pTest, mstp_port.DataLength == 0); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + ct_test(pTest, HeaderCRC == 0x73); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + ct_test(pTest, mstp_port.Index == 5); + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + ct_test(pTest, mstp_port.HeaderCRC == 0x55); + /* NotForUs */ + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + ct_test(pTest, len > 0); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.DataAvailable == false); + ct_test(pTest, mstp_port.SilenceTimer == 0); + ct_test(pTest, mstp_port.EventCount == EventCount); + } + ct_test(pTest, + mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC); + MSTP_Receive_Frame_FSM(&mstp_port); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == true); + ct_test(pTest, mstp_port.ReceivedValidFrame == false); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + /* Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data, 0, sizeof(data)); + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, my_mac, /* destination */ + my_mac, /* source */ + data, /* data */ + sizeof(data)); /* data size */ + ct_test(pTest, len > 0); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + ct_test(pTest, mstp_port.DataLength == sizeof(data)); + ct_test(pTest, mstp_port.ReceivedInvalidFrame == false); + ct_test(pTest, mstp_port.ReceivedValidFrame == true); + ct_test(pTest, mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE); + + return; +} + +void testMasterNodeFSM(Test * pTest) +{ + volatile struct mstp_port_struct_t mstp_port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + + MSTP_Init(&mstp_port, my_mac); + ct_test(pTest, mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE); + +} + +#endif + +#ifdef TEST_MSTP +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("mstp", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testReceiveNodeFSM); + assert(rc); + rc = ct_addTestFunction(pTest, testMasterNodeFSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/mstp.h b/bacnet-stack-0-3-0/ports/rtos32/mstp.h new file mode 100644 index 00000000..d7c74bfd --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/mstp.h @@ -0,0 +1,244 @@ +/*####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 MSTP_H +#define MSTP_H + +#include +#include +#include +#include "bacdef.h" +#include "dlmstp.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#define MSTP_BROADCAST_ADDRESS 255 + +/* MS/TP Frame Type */ +/* Frame Types 8 through 127 are reserved by ASHRAE. */ +#define FRAME_TYPE_TOKEN 0 +#define FRAME_TYPE_POLL_FOR_MASTER 1 +#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2 +#define FRAME_TYPE_TEST_REQUEST 3 +#define FRAME_TYPE_TEST_RESPONSE 4 +#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5 +#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6 +#define FRAME_TYPE_REPLY_POSTPONED 7 +/* Frame Types 128 through 255: Proprietary Frames */ +/* These frames are available to vendors as proprietary (non-BACnet) frames. */ +/* The first two octets of the Data field shall specify the unique vendor */ +/* identification code, most significant octet first, for the type of */ +/* vendor-proprietary frame to be conveyed. The length of the data portion */ +/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +#define FRAME_TYPE_PROPRIETARY_MIN 128 +#define FRAME_TYPE_PROPRIETARY_MAX 255 +/* The initial CRC16 checksum value */ +#define CRC16_INITIAL_VALUE (0xFFFF) + + +/* receive FSM states */ +typedef enum { + MSTP_RECEIVE_STATE_IDLE, + MSTP_RECEIVE_STATE_PREAMBLE, + MSTP_RECEIVE_STATE_HEADER, + MSTP_RECEIVE_STATE_HEADER_CRC, + MSTP_RECEIVE_STATE_DATA +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE, + MSTP_MASTER_STATE_IDLE, + MSTP_MASTER_STATE_USE_TOKEN, + MSTP_MASTER_STATE_WAIT_FOR_REPLY, + MSTP_MASTER_STATE_DONE_WITH_TOKEN, + MSTP_MASTER_STATE_PASS_TOKEN, + MSTP_MASTER_STATE_NO_TOKEN, + MSTP_MASTER_STATE_POLL_FOR_MASTER, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST +} MSTP_MASTER_STATE; + +struct mstp_port_struct_t { + MSTP_RECEIVE_STATE receive_state; + /* When a master node is powered up or reset, */ + /* it shall unconditionally enter the INITIALIZE state. */ + MSTP_MASTER_STATE master_state; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an error is detected during the reception of a frame. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceiveError:1; + /* There is data in the buffer */ + unsigned DataAvailable:1; + unsigned FramingError:1; /* TRUE if we got a framing error */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* After receiving a frame this value will be TRUE until Tturnaround */ + /* has expired */ + unsigned Turn_Around_Waiting:1; + /* stores the latest received data */ + uint8_t DataRegister; + /* Used to accumulate the CRC on the data field of a frame. */ + uint16_t DataCRC; + /* Used to store the data length of a received frame. */ + unsigned DataLength; + /* Used to store the destination address of a received frame. */ + uint8_t DestinationAddress; + /* Used to count the number of received octets or errors. */ + /* This is used in the detection of link activity. */ + /* Compared to Nmin_octets */ + uint8_t EventCount; + /* Used to store the frame type of a received frame. */ + uint8_t FrameType; + /* The number of frames sent by this node during a single token hold. */ + /* When this counter reaches the value Nmax_info_frames, the node must */ + /* pass the token. */ + unsigned FrameCount; + /* Used to accumulate the CRC on the header of a frame. */ + uint8_t HeaderCRC; + /* Used as an index by the Receive State Machine, up to a maximum value of */ + /* InputBufferSize. */ + unsigned Index; + /* An array of octets, used to store octets as they are received. */ + /* InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* The maximum size of a frame is 501 octets. */ + uint8_t InputBuffer[MAX_MPDU]; + /* "Next Station," the MAC address of the node to which This Station passes */ + /* the token. If the Next_Station is unknown, Next_Station shall be equal to */ + /* This_Station. */ + uint8_t Next_Station; + /* "Poll Station," the MAC address of the node to which This Station last */ + /* sent a Poll For Master. This is used during token maintenance. */ + uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + unsigned RetryCount; + /* A timer with nominal 5 millisecond resolution used to measure and */ + /* generate silence on the medium between octets. It is incremented by a */ + /* timer process and is cleared by the Receive State Machine when activity */ + /* is detected and by the SendFrame procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily */ + /* synchronized to other machine events, a timer value of N will actually */ + /* denote intervals between N-1 and N */ + uint16_t SilenceTimer; + + /* A timer used to measure and generate Reply Postponed frames. It is */ + /* incremented by a timer process and is cleared by the Master Node State */ + /* Machine when a Data Expecting Reply Answer activity is completed. */ + uint16_t ReplyPostponedTimer; + + /* Used to store the Source Address of a received frame. */ + uint8_t SourceAddress; + + /* The number of tokens received by this node. When this counter reaches the */ + /* value Npoll, the node polls the address range between TS and NS for */ + /* additional master nodes. TokenCount is set to zero at the end of the */ + /* polling process. */ + unsigned TokenCount; + + /* "This Station," the MAC address of this node. TS is generally read from a */ + /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ + /* 0 to 254. The value 255 is used to denote broadcast when used as a */ + /* destination address but is not allowed as a value for TS. */ + uint8_t This_Station; + + /* This parameter represents the value of the Max_Info_Frames property of */ + /* the node's Device object. The value of Max_Info_Frames specifies the */ + /* maximum number of information frames the node may send before it must */ + /* pass the token. Max_Info_Frames may have different values on different */ + /* nodes. This may be used to allocate more or less of the available link */ + /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ + /* node, its value shall be 1. */ + unsigned Nmax_info_frames; + + /* This parameter represents the value of the Max_Master property of the */ + /* node's Device object. The value of Max_Master specifies the highest */ + /* allowable address for master nodes. The value of Max_Master shall be */ + /* less than or equal to 127. If Max_Master is not writable in a node, */ + /* its value shall be 127. */ + unsigned Nmax_master; + + /* An array of octets, used to store PDU octets prior to being transmitted. */ + /* This array is only used for APDU messages */ + uint8_t TxBuffer[MAX_MPDU]; + unsigned TxLength; + bool TxReady; /* true if ready to be sent or received */ + uint8_t TxFrameType; /* type of message - needed by MS/TP */ +}; + +#define DEFAULT_MAX_INFO_FRAMES 1 +#define DEFAULT_MAX_MASTER 127 + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +#define Tturnaround 40; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port, + uint8_t this_station_mac); + void MSTP_Millisecond_Timer(volatile struct mstp_port_struct_t + *mstp_port); + void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t + *mstp_port); + bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t + *mstp_port); + + /* returns true if line is active */ + bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port); + + unsigned MSTP_Create_Frame(uint8_t * buffer, /* where frame is loaded */ + unsigned buffer_len, /* amount of space available */ + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + unsigned data_len); /* number of bytes of data (up to 501) */ + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/net.h b/bacnet-stack-0-3-0/ports/rtos32/net.h new file mode 100644 index 00000000..5f6b680d --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/net.h @@ -0,0 +1,80 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 NET_H +#define NET_H + +/* network file for RTOS-32 from On-Time */ +#define WIN32_LEAN_AND_MEAN +#define STRICT + +#include +#include +#include +#include + +#include "bip.h" +#ifndef HOST +#include +#include "netcfg.h" +#include +#include +#include +#include + +/* + * Definitions of bits in internet address integers. + * On subnets, the decomposition of addresses to host and net parts + * is done according to subnet mask, not the masks here. + */ +#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0) +#define IN_CLASSA_NET 0xff000000 +#define IN_CLASSA_NSHIFT 24 +#define IN_CLASSA_HOST 0x00ffffff +#define IN_CLASSA_MAX 128 + +#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) +#define IN_CLASSB_NET 0xffff0000 +#define IN_CLASSB_NSHIFT 16 +#define IN_CLASSB_HOST 0x0000ffff +#define IN_CLASSB_MAX 65536 + +#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) +#define IN_CLASSC_NET 0xffffff00 +#define IN_CLASSC_NSHIFT 8 +#define IN_CLASSC_HOST 0x000000ff + +#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000) +#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */ +#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */ +#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */ +#define IN_MULTICAST(i) IN_CLASSD(i) + +#else +#include +#endif +#define close closesocket +typedef size_t socklen_t; + +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/netcfg.h b/bacnet-stack-0-3-0/ports/rtos32/netcfg.h new file mode 100644 index 00000000..48e85000 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/netcfg.h @@ -0,0 +1,104 @@ +/**************************************************************************/ +/* */ +/* File: NetCfg.h Copyright (c) 1996,2004 */ +/* Version: 4.0 On Time Informatik GmbH */ +/* */ +/* */ +/* On Time /////////////----- */ +/* Informatik GmbH ///////////// */ +/* --------------------------------------------------///////////// */ +/* Real-Time and System Software */ +/* */ +/**************************************************************************/ + +/* Network environment configuration file for the On Time RTOS-32 RTIP-32 + demos. + + By default, the RTIP-32 demos use static IP address assignment. If this is + not what you want, uncomment either #define AUTO_IP or #define DHCP. In all + cases, make sure the IP addresses (NetMask, TargetIP, DefaultGateway, + DNSServer, etc) given in the respective section below are all correct for + your select and LAN configuration. If you choose to use DHCP, the library + Dhcpc.lib must also be linked. + + Please define symbol DEVICE_ID to match your target's ethernet + card and make sure that the card's hardware resource assigments are + correct (for PCI cards, the drivers will determine this information + automatically). + +*/ + +/* #define AUTO_IP // use xn_autoip() to get an IP address */ +/* #define DHCP // if you enable this, you must also link library dhcpc.lib */ + +#if defined(AUTO_IP) /* use xn_autoip() to get an IP address */ +static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */ +static BYTE NetMask[] = { 255, 255, 255, 0 }; +static BYTE MinIP[] = { 192, 168, 0, 128 }; +static BYTE MaxIP[] = { 192, 168, 0, 255 }; +static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */ +static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */ +#elif defined(DHCP) /* use DHCP */ +#include +static BYTE TargetIP[] = { 0, 0, 0, 0 }; /* will be filled at run-time */ +#else /* static IP address assignment (default) */ +static BYTE TargetIP[] = { 192, 168, 0, 50 }; +static BYTE NetMask[] = { 255, 255, 255, 0 }; +static BYTE DefaultGateway[] = { 192, 168, 0, 1 }; /* set to zero if not available or required */ +static BYTE DNSServer[] = { 192, 168, 0, 1 }; /* ditto */ +#endif + +#define DEVICE_ID DAVICOM_DEVICE /* define your device type here */ + +#ifndef DEVICE_ID +#error You must define Ethernet driver/resources and IP address/net mask here +#endif + +/* The following values are ignored for PCI devices (the BIOS supplies */ +/* them), but they must be set correctly for ISA/PCMCIA systems and for */ +/* PCI devices if you do not have a BIOS */ + +#define ED_IO_ADD 0x300 /* I/O address of the device */ +#define ED_IRQ 5 /* IRQ of the device */ +#define ED_MEM_ADD 0 /* Memory Window (only some devices) */ + +/* Define function to pull in the required driver */ + +#if DEVICE_ID == NE2000_DEVICE +#define BIND_DRIVER xn_bind_ne2000 +#elif DEVICE_ID == N83815_DEVICE +#define BIND_DRIVER xn_bind_n83815 +#elif DEVICE_ID == TC90X_DEVICE +#define BIND_DRIVER xn_bind_tc90x +#elif DEVICE_ID == SMC91C9X_DEVICE +#define BIND_DRIVER xn_bind_smc91c9x +#elif DEVICE_ID == LANCE_DEVICE +#define BIND_DRIVER xn_bind_rtlance +#elif DEVICE_ID == LANCE_ISA_DEVICE +#define BIND_DRIVER xn_bind_lance_isa +#elif DEVICE_ID == LAN_CS89X0_DEVICE +#define BIND_DRIVER xn_bind_cs +#elif DEVICE_ID == I82559_DEVICE +#define BIND_DRIVER xn_bind_i82559 +#elif DEVICE_ID == R8139_DEVICE +#define BIND_DRIVER xn_bind_r8139 +#elif DEVICE_ID == DAVICOM_DEVICE +#define BIND_DRIVER xn_bind_davicom +#elif DEVICE_ID == RHINE_DEVICE +#define BIND_DRIVER xn_bind_rhine +#elif DEVICE_ID == AX172_DEVICE +#include /* must also link Rtusb.lib and UsbInit.cpp */ +#define BIND_DRIVER xn_bind_ax172 +#elif DEVICE_ID == AX772_DEVICE +#include /* must also link Rtusb.lib and UsbInit.cpp */ +#define BIND_DRIVER xn_bind_ax772 +#elif DEVICE_ID == PRISM_DEVICE +#include /* must also link Wlan.lib */ +#define BIND_DRIVER xn_bind_prism +#elif DEVICE_ID == PRISM_PCMCIA_DEVICE +#include +#include /* must also link Wlan.lib */ +#define BIND_DRIVER xn_bind_prism_pcmcia +#else +#error Invalid DEVICE_ID value +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/rs485.c b/bacnet-stack-0-3-0/ports/rtos32/rs485.c new file mode 100644 index 00000000..fa5dde73 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/rs485.c @@ -0,0 +1,218 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ +#if PRINT_ENABLED +#define PRINT_ENABLED_RS485 1 +#else +#define PRINT_ENABLED_RS485 0 +#endif + +#include +#include +#include +#include +#if PRINT_ENABLED_RS485 +#include +#endif +#include "mstp.h" + +/* note: use the RTKernel-C API so that it can use this library */ + +#define RS485_IO_ENABLE(p) ModemControl(p,0,DTR); +#define RS485_TRANSMIT_ENABLE(p) ModemControl(p,1,RTS); +#define RS485_RECEIVE_ENABLE(p) ModemControl(p,0,RTS); + +/* COM port number - COM1 = 0 */ +static int RS485_Port = COM2; +/* baud rate */ +static long RS485_Baud = 9600; +/* io base address */ +static long RS485_Base = 0; +/* hardware IRQ number */ +static long RS485_IRQ_Number = 0; + +#if PRINT_ENABLED_RS485 +static FineTime RS485_Debug_Transmit_Timer; +#endif + +#if PRINT_ENABLED_RS485 +void RS485_Print_Frame(int port, FineTime timer, uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ + uint16_t i; /* byte counter */ + unsigned long duration; /* measures the time from last output to this one */ + unsigned long seconds; + unsigned long milliseconds; + + duration = ElapsedMilliSecs(timer); + seconds = duration / 1000U; + milliseconds = duration - (seconds * 1000U); + fprintf(stderr, "%0lu.%03lu: COM%d:", seconds, milliseconds, port + 1); + for (i = 0; i < nbytes; i++) { + unsigned value; + value = buffer[i]; + fprintf(stderr, " %02X", value); + } + fprintf(stderr, "\n"); + fflush(stderr); +} +#endif + +static void RS485_Standard_Port_Settings(long port, long *pIRQ, + long *pBase) +{ + switch (port) { + case COM1: + *pBase = (long) 0x3F8; + *pIRQ = 4L; + break; + case COM2: + *pBase = (long) 0x2F8; + *pIRQ = 3L; + break; + case COM3: + *pBase = (long) 0x3E8; + *pIRQ = 4L; + break; + case COM4: + *pBase = (long) 0x2E8; + *pIRQ = 3L; + break; + default: + break; + } +} + +static int TestCOMPort(int Base) +{ /* base address of UART */ + int i; + + for (i = 0; i < 256; i++) { + RTOut(Base + 7, (BYTE) i); /* write scratch register */ + RTOut(Base + 1, RTIn(Base + 1)); /* read/write IER */ + if (RTIn(Base + 7) != i) /* check scratch register */ + return FALSE; + } + return TRUE; +} + +static RS485_Open_Port(int port, /* COM port number - COM1 = 0 */ + long baud, /* baud rate */ + unsigned base, /* io base address */ + int irq) +{ /* hardware IRQ number */ + if (!TestCOMPort(base)) + return; + + /* setup the COM IO */ + SetIOBase(port, base); + SetIRQ(port, irq); + + if (irq < 8) + RTKIRQTopPriority(irq, 9); + + InitPort(port, baud, PARITY_NONE, 1, 8); + + if (HasFIFO(port)) + EnableFIFO(port, 8); + EnableCOMInterrupt(port, 1024 * 4); + + /* enable the 485 via the DTR pin */ + RS485_IO_ENABLE(port); + RS485_RECEIVE_ENABLE(port); +#if PRINT_ENABLED_RS485 + fprintf(stderr, "RS485: COM%d Enabled\r\n", port + 1); +#endif + + return; +} + +void RS485_Initialize(void) +{ +#if PRINT_ENABLED_RS485 + MarkTime(&RS485_Debug_Transmit_Timer); +#endif + RS485_Standard_Port_Settings(RS485_Port, &RS485_IRQ_Number, + &RS485_Base); + RS485_Open_Port(RS485_Port, RS485_Baud, RS485_Base, RS485_IRQ_Number); +} + +void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes) +{ /* number of bytes of data (up to 501) */ + bool status = true; /* return value */ + + RS485_TRANSMIT_ENABLE(RS485_Port); + SendBlock(RS485_Port, (char *) buffer, nbytes); + /* need to wait at least 9600 baud * 512 bytes = 54mS */ + (void) WaitSendBufferEmpty(RS485_Port, MilliSecsToTicks(200)); + while (!(LineStatus(RS485_Port) & TX_SHIFT_EMPTY)) + RTKScheduler(); + RS485_RECEIVE_ENABLE(RS485_Port); + /* SilenceTimer is cleared by the Receive State Machine when + activity is detected and by the SendFrame procedure as each + octet is transmitted. */ + mstp_port->SilenceTimer = 0; +#if PRINT_ENABLED_RS485 + RS485_Print_Frame(RS485_Port, RS485_Debug_Transmit_Timer, buffer, /* frame to send (up to 501 bytes of data) */ + nbytes); + MarkTime(&RS485_Debug_Transmit_Timer); +#endif + + return; +} + +void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +{ /* port specific data */ + COMData com_data = 0; /* byte from COM driver */ + unsigned timeout = 1; /* milliseconds to wait for a character */ + static Duration ticks = 0; /* duration to wait for data */ + + if (mstp_port->ReceiveError) { + /* wait for state machine to clear this */ + RTKScheduler(); + } + /* wait for state machine to read from the DataRegister */ + else if (!mstp_port->DataAvailable) { + if (!ticks) { + ticks = MilliSecsToTicks(timeout); + if (!ticks) + ticks = 1; + } + /* check for data */ + if (RTKGetTimed(ReceiveBuffer[RS485_Port], &com_data, ticks)) { + /* if error, */ + if (com_data & (COM_OVERRUN << 8)) + mstp_port->ReceiveError = true; + else if (com_data & (COM_FRAME << 8)) + mstp_port->ReceiveError = true; + else { + mstp_port->DataRegister = com_data & 0x00FF; + mstp_port->DataAvailable = true; + } + } + } else + RTKScheduler(); +} diff --git a/bacnet-stack-0-3-0/ports/rtos32/rs485.h b/bacnet-stack-0-3-0/ports/rtos32/rs485.h new file mode 100644 index 00000000..1f0c18db --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/rs485.h @@ -0,0 +1,57 @@ +/*####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 RS485_H +#define RS485_H + +#include +#include "mstp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize(void); + + void RS485_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ + uint16_t nbytes); /* number of bytes of data (up to 501) */ + + void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/setvars.bat b/bacnet-stack-0-3-0/ports/rtos32/setvars.bat new file mode 100644 index 00000000..404c1998 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/setvars.bat @@ -0,0 +1,3 @@ +set BORLAND_DIR=\bc5 +set RTOS32_DIR=\code\rtos32 + diff --git a/bacnet-stack-0-3-0/ports/rtos32/software.cfg b/bacnet-stack-0-3-0/ports/rtos32/software.cfg new file mode 100644 index 00000000..9bde4528 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/software.cfg @@ -0,0 +1,61 @@ +// Configuration files for the application and Borland C/C++. + +// Some general parameters for this file are: + +// * The program will run under the control of the debugger or is +// downloaded using RTRun. +// * Paging is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The program is placed in high (extended) memory. +// * Unused low memory is remapped and appended to the high memory area. +// * The Turbo Debugger symbol tables are pulled in to support +// task positions at source level. + +@HARDWARE.CFG + +// Either use the monitor, or create bootable code. +#ifdef MONITOR + Reserve Monitor // leave room for Debug Monitor +#elifdef DEBUGDOS + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk + Locate BootData BootData LowMem // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges + VideoRAM ColorText // program output sent to Graphic Card +#else + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk + Locate BootData BootData LowMem 0 16 // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges +// VideoRAM ColorText // program output sent to Graphic Card + VideoRAM None // program output sent to file and host +#endif + +FillRAM HeapMem // remap unused RAM + +Locate Header Header LowMem // application header +Locate PageTable Paging LowMem 20k // paging to use this +Locate NTSection CODE ProgMem->HighMem // code section +Locate NTSection DATA ProgMem->HighMem // data section +Locate NTSection .tls ProgMem->HighMem // TLS data section +Locate NTSection .rdata ProgMem->HighMem // TLS directory +Locate Stack Stack StackMem->LowMem 6k // stack space for main() +Locate Heap Heap HeapMem // and the rest for the heap + +// Compression needed if we are short on disk space - but shortens download +// Note that this is discardable, unless we use -d- option of RTLoc +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy Paging LowMem // compress Paging +Locate Copy CODE HighMem // compress CODE +Locate Copy DATA HighMem // compress DATA + +Locate Nothing FloppyDMA MoreLowMem 18k 64k ReadWrite // floppy driver + +Init _Init // do some standard initializations (see init.c) + +CommandLine "bacnet.exe" \ No newline at end of file diff --git a/bacnet-stack-0-3-0/ports/rtos32/stdbool.h b/bacnet-stack-0-3-0/ports/rtos32/stdbool.h new file mode 100644 index 00000000..2b7511a6 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef int _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/bacnet-stack-0-3-0/ports/rtos32/stdint.h b/bacnet-stack-0-3-0/ports/rtos32/stdint.h new file mode 100644 index 00000000..9c003d0a --- /dev/null +++ b/bacnet-stack-0-3-0/ports/rtos32/stdint.h @@ -0,0 +1,19 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef STDINT_H +#define STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/bacnet-stack-0-3-0/ports/win32/MAKEFILE.MAK b/bacnet-stack-0-3-0/ports/win32/MAKEFILE.MAK new file mode 100644 index 00000000..06287917 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/MAKEFILE.MAK @@ -0,0 +1,151 @@ +# +# Simple makefile to build an RTB executable for RTOS-32 +# +# 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 = bacnet +PRODUCT_EXE = $(PRODUCT).exe + +# Choose the Data Link Layer to Enable +DEFINES = -DBACDL_BIP=1;TSM_ENABLED=1;BIG_ENDIAN=0 + +SRCS = main.c bip-init.c \ + ..\..\bip.c \ + ..\..\demo\handler\h_iam.c \ + ..\..\demo\handler\h_whois.c \ + ..\..\demo\handler\h_wp.c \ + ..\..\demo\handler\h_rp.c \ + ..\..\demo\handler\h_rp_a.c \ + ..\..\demo\handler\noserv.c \ + ..\..\demo\handler\txbuf.c \ + ..\..\demo\handler\s_rp.c \ + ..\..\demo\handler\s_whois.c \ + ..\..\bacdcode.c \ + ..\..\bacapp.c \ + ..\..\bacstr.c \ + ..\..\bactext.c \ + ..\..\indtext.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\dcc.c \ + ..\..\rp.c \ + ..\..\wp.c \ + ..\..\arf.c \ + ..\..\awf.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\lsp.c \ + ..\..\demo\object\mso.c \ + ..\..\datalink.c \ + ..\..\tsm.c \ + ..\..\address.c \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +BCC_CFG = bcc32.cfg +CC = $(BORLAND_DIR)\bin\bcc32 +$(BCC_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\handler\;..\..\demo\object\;. + +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 : $(BCC_CFG) $(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 *.obj + del ..\..\*.obj + del ..\..\demo\handler\*.obj + del ..\..\demo\object\*.obj + del $(PRODUCT_EXE) + del *.map + del $(BCC_CFG) + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + $(CC) -o$@ $< + +# Compiler configuration file +$(BCC_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 diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet.ide b/bacnet-stack-0-3-0/ports/win32/bacnet.ide new file mode 100644 index 00000000..566cabcf Binary files /dev/null and b/bacnet-stack-0-3-0/ports/win32/bacnet.ide differ diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsp b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsp new file mode 100644 index 00000000..4fb4deee --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsp @@ -0,0 +1,460 @@ +# Microsoft Developer Studio Project File - Name="bacnet" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=bacnet - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "bacnet.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bacnet.mak" CFG="bacnet - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "bacnet - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "bacnet - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "bacnet - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "NDEBUG" /D "BACDL_BIP" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D BACAPP_PRINT_ENABLED=1 /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "bacnet - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D BACDL_BIP=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D USE_INADDR=0 /FR /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "bacnet - Win32 Release" +# Name "bacnet - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\..\abort.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\address.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ai.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ao.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\apdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\arf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\av.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacapp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacdcode.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacerror.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bacfile.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacstr.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bactext.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bi.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\bigend.c +# End Source File +# Begin Source File + +SOURCE="..\bip-init.c" +# End Source File +# Begin Source File + +SOURCE=..\..\..\bip.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bo.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bv.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\crc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\datetime.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\dcc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\device.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_arf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_arf_a.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_rp_a.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\h_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\iam.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\indtext.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lsp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\lsp.h +# End Source File +# Begin Source File + +SOURCE=..\main.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\mso.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\noserv.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\npdu.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\reject.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\ringbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_rp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\s_wp.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\tsm.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\handler\txbuf.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\whois.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\wp.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\..\..\abort.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\address.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ai.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\ao.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\apdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\arcnet.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacapp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacdcode.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacdef.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacenum.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacerror.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bacfile.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bacstr.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bactext.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bigend.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bip.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bits.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bo.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\bv.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\bytes.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\config.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\crc.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\datalink.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\datetime.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\device.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\ethernet.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\handlers.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\iam.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\demo\object\mso.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\mstp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\npdu.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\reject.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\ringbuf.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\rp.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\rs485.h +# End Source File +# Begin Source File + +SOURCE=..\stdbool.h +# End Source File +# Begin Source File + +SOURCE=..\stdint.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\tsm.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\whois.h +# End Source File +# Begin Source File + +SOURCE=..\..\..\wp.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsw b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsw new file mode 100644 index 00000000..e9d249d7 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "bacnet"=".\bacnet.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.ncb b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.ncb new file mode 100644 index 00000000..4914a281 Binary files /dev/null and b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.ncb differ diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.opt b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.opt new file mode 100644 index 00000000..9821af52 Binary files /dev/null and b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.opt differ diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.plg b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.plg new file mode 100644 index 00000000..284390b7 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.plg @@ -0,0 +1,136 @@ + + +
+

Build Log

+

+--------------------Configuration: bacnet - Win32 Debug-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP862.tmp" with contents +[ +/nologo /MLd /W3 /Gm /GX /ZI /Od /I "..\..\.." /I ".." /I "..\..\..\demo\object\\" /I "..\..\..\demo\handler\\" /D "_DEBUG" /D BACDL_BIP=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D TSM_ENABLED=1 /D PRINT_ENABLED=1 /D BIG_ENDIAN=0 /D USE_INADDR=0 /FR"Debug/" /Fo"Debug/" /Fd"Debug/" /FD /GZ /c +"C:\code\bacnet-stack\bacapp.c" +"C:\code\bacnet-stack\bacdcode.c" +] +Creating command line "cl.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP862.tmp" +Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP863.tmp" with contents +[ +kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/bacnet.pdb" /debug /machine:I386 /out:"Debug/bacnet.exe" /pdbtype:sept +".\Debug\abort.obj" +".\Debug\address.obj" +".\Debug\ai.obj" +".\Debug\ao.obj" +".\Debug\apdu.obj" +".\Debug\arf.obj" +".\Debug\av.obj" +".\Debug\bacapp.obj" +".\Debug\bacdcode.obj" +".\Debug\bacerror.obj" +".\Debug\bacfile.obj" +".\Debug\bacstr.obj" +".\Debug\bactext.obj" +".\Debug\bi.obj" +".\Debug\bigend.obj" +".\Debug\bip-init.obj" +".\Debug\bip.obj" +".\Debug\bo.obj" +".\Debug\bv.obj" +".\Debug\crc.obj" +".\Debug\datetime.obj" +".\Debug\dcc.obj" +".\Debug\device.obj" +".\Debug\h_arf.obj" +".\Debug\h_arf_a.obj" +".\Debug\h_iam.obj" +".\Debug\h_rp.obj" +".\Debug\h_rp_a.obj" +".\Debug\h_whois.obj" +".\Debug\h_wp.obj" +".\Debug\iam.obj" +".\Debug\indtext.obj" +".\Debug\lc.obj" +".\Debug\lsp.obj" +".\Debug\main.obj" +".\Debug\mso.obj" +".\Debug\noserv.obj" +".\Debug\npdu.obj" +".\Debug\reject.obj" +".\Debug\ringbuf.obj" +".\Debug\rp.obj" +".\Debug\s_rp.obj" +".\Debug\s_whois.obj" +".\Debug\s_wp.obj" +".\Debug\tsm.obj" +".\Debug\txbuf.obj" +".\Debug\whois.obj" +".\Debug\wp.obj" +] +Creating command line "link.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP863.tmp" +

Output Window

+Compiling... +bacapp.c +bacdcode.c +Generating Code... +Linking... +Creating temporary file "C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP864.tmp" with contents +[ +/nologo /o"Debug/bacnet.bsc" +".\Debug\abort.sbr" +".\Debug\address.sbr" +".\Debug\ai.sbr" +".\Debug\ao.sbr" +".\Debug\apdu.sbr" +".\Debug\arf.sbr" +".\Debug\av.sbr" +".\Debug\bacapp.sbr" +".\Debug\bacdcode.sbr" +".\Debug\bacerror.sbr" +".\Debug\bacfile.sbr" +".\Debug\bacstr.sbr" +".\Debug\bactext.sbr" +".\Debug\bi.sbr" +".\Debug\bigend.sbr" +".\Debug\bip-init.sbr" +".\Debug\bip.sbr" +".\Debug\bo.sbr" +".\Debug\bv.sbr" +".\Debug\crc.sbr" +".\Debug\datetime.sbr" +".\Debug\dcc.sbr" +".\Debug\device.sbr" +".\Debug\h_arf.sbr" +".\Debug\h_arf_a.sbr" +".\Debug\h_iam.sbr" +".\Debug\h_rp.sbr" +".\Debug\h_rp_a.sbr" +".\Debug\h_whois.sbr" +".\Debug\h_wp.sbr" +".\Debug\iam.sbr" +".\Debug\indtext.sbr" +".\Debug\lc.sbr" +".\Debug\lsp.sbr" +".\Debug\main.sbr" +".\Debug\mso.sbr" +".\Debug\noserv.sbr" +".\Debug\npdu.sbr" +".\Debug\reject.sbr" +".\Debug\ringbuf.sbr" +".\Debug\rp.sbr" +".\Debug\s_rp.sbr" +".\Debug\s_whois.sbr" +".\Debug\s_wp.sbr" +".\Debug\tsm.sbr" +".\Debug\txbuf.sbr" +".\Debug\whois.sbr" +".\Debug\wp.sbr"] +Creating command line "bscmake.exe @C:\DOCUME~1\stk01\LOCALS~1\Temp\RSP864.tmp" +Creating browse info file... +

Output Window

+ + + +

Results

+bacnet.exe - 0 error(s), 0 warning(s) +
+ + diff --git a/bacnet-stack-0-3-0/ports/win32/bacnet/readme.txt b/bacnet-stack-0-3-0/ports/win32/bacnet/readme.txt new file mode 100644 index 00000000..ef74afff --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/bacnet/readme.txt @@ -0,0 +1,113 @@ +BACnet Stack - SourceForge.net +Build for Visual C++ 6.0 + +When building the BACnet stack using Visual C++ compiler, +there are some settings that are important. + +Q. Are there some global configuration options for this BACnet stack? + +A. The BACnet stack uses some preprocessor defines to configure +a number of subtle personalities. +PRINT_ENABLED=1 - enables printing to stdio +BIG_ENDIAN=0 - chooses the BACnet encoding and decoding order +BACDL_BIP=1 - chooses BACnet/IP for the datalink layer +BACDL_ETHERNET=0 - chooses BACnet Ethernet for the datalink layer +BACDL_ARCNET=0 - chooses BACnet ARCNET for the datalink layer +BACDL_MSTP=0 - chooses BACnet MS/TP for the datalink layer +USE_INADDR=1 - uses INADDR_BROADCAST for broadcast rather than CLASSx +TSM_ENABLED=1 - enables the Transaction State Machine for clients +BIP_DEBUG=1 - enables print statements for debugging +In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type OPTION_NAME=1 or OPTION_NAME=0 in that edit box + using a comma to separate multiple options. +7. Press OK +8. Compile the entire project again... + +Q. MSVC refuses to open bacnet.dsw and bacnet.dsp. + +A. bacnet.dsw and bacnet.dsp are text files that were retrieved +from CVS on a unix client and are now in unix text file format since +they end with a "\r\n" rather than "\n". Use the unix2dos commandline +tool to convert them back to dos: +unix2dos bacnet.dsw +unix2dos bacnet.dsp + +Q. error LNK2001: unresolved external symbol _WinMain@16 + +A. The demo ports/win32/main.c was designed as a Win32 Console +Application. If you want to change it to a Windows GUI application, +you will have to add all the Windows GUI code, including WinMain(). +I recommend that you use a framework, such as WxWidgets/WxWindows, +but this has not been done yet. + +Q. error C1083: Cannot open include file: 'stdint.h': No such file + +A. The BACnet stack uses some header files, and Visual C++ needs to know +where they are: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: Preprocessor +5. You can see the "Additional include directories:" box +6. Type the path to stdint.h in that edit box (using a comma if necessary) +7. Type the path to bacdcode.h in that edit box (using a comma if necessary) +In my system, the paths look like: +c:\code\bacnet-stack\,c:\code\bacnet-stack\ports\win32\, +c:\code\bacnet-stack\demo\handler\,c:\code\bacnet-stack\demo\object\ +8. Press OK +9. Compile the project again... + +Q. error C2065: 'MAX_MPDU' : undeclared identifier + +A. The BACnet stack uses a preprocessor define to configure +its datalink layer. In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type BACDL_BIP=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol _bacapp_print + +A. The BACnet stack uses a preprocessor define to configure +printing to stdio. In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type PRINT_ENABLED=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... + +Q. error LNK2001: unresolved external symbol __imp__closesocket@4 + +A. Visual C++ needs to have the Winsock library to be happy: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "Link" tab (4th Tab) +4. You can see "Object/library modules:" edit box +5. Type Wsock32.LIB in that edit box +6. Press OK +7. Compile the entire project again... + +Q. error C2061: in file tsm.c +A. The BACnet stack uses a preprocessor define to configure +client functionality in the Transaction State Machine (TSM). +In Visual C++, add a Preprocessor Definition by: +1. Select "Project" menu +2. Select "Settings..." +3. Select the "C/C++" tab (3rd Tab) +4. Select the Category: General +5. You can see the "Preprocessor Definitions:" box +6. Type TSM_ENABLED=1 in that edit box (using a comma if necessary) +7. Press OK +8. Compile the entire project again... diff --git a/bacnet-stack-0-3-0/ports/win32/bip-init.c b/bacnet-stack-0-3-0/ports/win32/bip-init.c new file mode 100644 index 00000000..cf34a450 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/bip-init.c @@ -0,0 +1,212 @@ +/*####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####*/ + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 + +#include +#include +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "net.h" + +/* To fill a need, we invent the gethostaddr() function. */ +static long gethostaddr(void) +{ + struct hostent *host_ent; + char host_name[255]; + + if (gethostname(host_name, sizeof(host_name)) != 0) + return -1; +#ifdef BIP_DEBUG + printf("host name: %s\n", host_name); +#endif + if ((host_ent = gethostbyname(host_name)) == NULL) + return -1; + + return *(long *) host_ent->h_addr; +} + +static void set_broadcast_address(uint32_t net_address) +{ + long broadcast_address = 0; + long mask = 0; + + /* Note: sometimes INADDR_BROADCAST does not let me get + any unicast messages. Not sure why... */ +#if USE_INADDR + (void) net_address; + bip_set_broadcast_addr(INADDR_BROADCAST); +#else + if (IN_CLASSA(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSA_HOST) | IN_CLASSA_HOST; + else if (IN_CLASSB(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSB_HOST) | IN_CLASSB_HOST; + else if (IN_CLASSC(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSC_HOST) | IN_CLASSC_HOST; + else if (IN_CLASSD(ntohl(net_address))) + broadcast_address = + (ntohl(net_address) & ~IN_CLASSD_HOST) | IN_CLASSD_HOST; + else + broadcast_address = INADDR_BROADCAST; + bip_set_broadcast_addr(htonl(broadcast_address)); +#endif +} + +static void cleanup(void) +{ + WSACleanup(); +} + +void bip_set_interface(char *ifname) +{ + (void) ifname; + /* dummy function */ +} + +bool bip_init(void) +{ + int rv = 0; /* return from socket lib calls */ + struct sockaddr_in sin = { -1 }; + int value = 1; + int sock_fd = -1; + int Result; + int Code; + WSADATA wd; + struct in_addr address; + + Result = WSAStartup((1 << 8) | 1, &wd); + /*Result = WSAStartup(MAKEWORD(2,2), &wd); */ + if (Result != 0) { + Code = WSAGetLastError(); + printf("TCP/IP stack initialization failed, error code: %i\n", + Code); + exit(1); + } + atexit(cleanup); + + address.s_addr = gethostaddr(); + if (address.s_addr == (unsigned) -1) { + Code = WSAGetLastError(); + printf("Get host address failed, error code: %i\n", Code); + exit(1); + } +#ifdef BIP_DEBUG + printf("host address: %s\n", inet_ntoa(address)); +#endif + bip_set_addr(address.s_addr); + set_broadcast_address(address.s_addr); + + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) { + fprintf(stderr, "bip: failed to allocate a socket.\n"); + return false; + } + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, + (char *) &value, sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set REUSEADDR socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } + /* allow us to send a broadcast */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, + (char *) &value, sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set BROADCAST socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } +#if 0 + /* probably only for Apple... */ + /* rebind a port that is already in use. + Note: all users of the port must specify this flag */ + rv = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, + (char *) &value, sizeof(value)); + if (rv < 0) { + fprintf(stderr, "bip: failed to set REUSEPORT socket option.\n"); + close(sock_fd); + bip_set_socket(-1); + return false; + } +#endif + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; +#if USE_INADDR + /* by setting sin.sin_addr.s_addr to INADDR_ANY, + I am telling the IP stack to automatically fill + in the IP address of the machine the process + is running on. + + Some server computers have multiple IP addresses. + A socket bound to one of these will not accept + connections to another address. Frequently you prefer + to allow any one of the computer's IP addresses + to be used for connections. Use INADDR_ANY (0L) to + allow clients to connect using any one of the host's + IP addresses. + + Note: sometimes INADDR_ANY does not let me get + any unicast messages. Not sure why... */ + sin.sin_addr.s_addr = htonl(INADDR_ANY); +#else + /* or we could use the specific adapter address + note: already in network byte order */ + sin.sin_addr.s_addr = address.s_addr; +#endif + sin.sin_port = htons(bip_get_port()); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + rv = bind(sock_fd, + (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (rv < 0) { + fprintf(stderr, "bip: failed to bind to %s port %hd\n", + inet_ntoa(sin.sin_addr), bip_get_port()); + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} diff --git a/bacnet-stack-0-3-0/ports/win32/ethernet.c b/bacnet-stack-0-3-0/ports/win32/ethernet.c new file mode 100644 index 00000000..972b30b3 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/ethernet.c @@ -0,0 +1,455 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 Steve Karg, modified by Kevin Liao + + 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 /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include +#include +#include + +#include "bacdef.h" +#include "ethernet.h" +#include "bacdcode.h" + + +/* Uses WinPCap to access raw ethernet */ +/* Notes: */ +/* To make ethernet.c work under win32, you have to: */ +/* 1. install winpcap 3.1 development pack; */ +/* 2. install Microsoft Platform SDK Feb 2003. */ +/* 3. remove or modify functions used for log such as */ +/* "LogError()", "LogInfo()", which were implemented */ +/* as a wrapper of Log4cpp. */ +/* -- Kevin Liao */ + +/* includes for accessing ethernet by using winpcap */ +#include "pcap.h" +#include "packet32.h" +#include "ntddndis.h" +#include "remote-ext.h" + + +/* commonly used comparison address for ethernet */ +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +/* commonly used empty address for ethernet quick compare */ +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +/* my local device data - MAC address */ +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 }; + +/* couple of var for using winpcap */ +static char pcap_errbuf[PCAP_ERRBUF_SIZE + 1]; +static pcap_t *pcap_eth802_fp = NULL; /* 802.2 file handle, from winpcap */ +static unsigned eth_timeout = 100; + + +/* couple of external func for runtime error logging, you can simply */ +/* replace them with standard "printf(...)" */ +/* Logging extern functions: Info level */ +extern void LogInfo(const char *msg); +/* Logging extern functions: Error level*/ +extern void LogError(const char *msg); +/* Logging extern functions: Debug level*/ +extern void LogDebug(const char *msg); + + +bool ethernet_valid(void) +{ + return (pcap_eth802_fp != NULL); +} + +void ethernet_cleanup(void) +{ + if (pcap_eth802_fp) { + pcap_close(pcap_eth802_fp); + pcap_eth802_fp = NULL; + } + LogInfo("ethernet.c: ethernet_cleanup() ok.\n"); +} + +void ethernet_set_timeout(unsigned timeout) +{ + eth_timeout = timeout; +} + +/*---------------------------------------------------------------------- + Portable function to set a socket into nonblocking mode. + Calling this on a socket causes all future read() and write() calls on + that socket to do only as much as they can immediately, and return + without waiting. + If no data can be read or written, they return -1 and set errno + to EAGAIN (or EWOULDBLOCK). + Thanks to Bjorn Reese for this code. +----------------------------------------------------------------------*/ +/** + * We don't need to use this function since WinPCap has provided one + * named "pcap_setnonblock()". + * Kevin, 2006.08.15 + */ +/* +int setNonblocking(int fd) +{ + int flags; + + if (-1 == (flags = fcntl(fd, F_GETFL, 0))) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} +*/ + +bool ethernet_init(char *if_name) +{ + PPACKET_OID_DATA pOidData; + LPADAPTER lpAdapter; + pcap_if_t *pcap_all_if; + pcap_if_t *dev; + BOOLEAN result; + CHAR str[sizeof(PACKET_OID_DATA) + 128]; + int i; + char msgBuf[200]; + + if (ethernet_valid()) + ethernet_cleanup(); + + /** + * Find the interface user specified + */ + /* Retrieve the device list */ + if (pcap_findalldevs(&pcap_all_if, pcap_errbuf) == -1) { + sprintf(msgBuf, + "ethernet.c: error in pcap_findalldevs: %s\n", pcap_errbuf); + LogError(msgBuf); + return false; + } + /* Scan the list printing every entry */ + for (dev = pcap_all_if; dev; dev = dev->next) { + if (strcmp(if_name, dev->name) == 0) + break; + } + pcap_freealldevs(pcap_all_if); /* we don't need it anymore */ + if (dev == NULL) { + sprintf(msgBuf, + "ethernet.c: specified interface not found: %s\n", if_name); + LogError(msgBuf); + return false; + } + + /** + * Get local MAC address + */ + ZeroMemory(str, sizeof(PACKET_OID_DATA) + 128); + lpAdapter = PacketOpenAdapter(if_name); + if (lpAdapter == NULL) { + ethernet_cleanup(); + sprintf(msgBuf, + "ethernet.c: error in PacketOpenAdapter(\"%s\")\n", if_name); + LogError(msgBuf); + return false; + } + pOidData = (PPACKET_OID_DATA) str; + pOidData->Oid = OID_802_3_CURRENT_ADDRESS; + pOidData->Length = 6; + result = PacketRequest(lpAdapter, FALSE, pOidData); + if (!result) { + PacketCloseAdapter(lpAdapter); + ethernet_cleanup(); + LogError("ethernet.c: error in PacketRequest()\n"); + return false; + } + for (i = 0; i < 6; ++i) + Ethernet_MAC_Address[i] = pOidData->Data[i]; + PacketCloseAdapter(lpAdapter); + + /** + * Open interface for subsequent sending and receiving + */ + /* Open the output device */ + pcap_eth802_fp = pcap_open(if_name, /* name of the device */ + MAX_MPDU, /* portion of the packet to capture */ + PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */ + eth_timeout, /* read timeout */ + NULL, /* authentication on the remote machine */ + pcap_errbuf /* error buffer */ + ); + if (pcap_eth802_fp == NULL) { + PacketCloseAdapter(lpAdapter); + ethernet_cleanup(); + sprintf(msgBuf, + "ethernet.c: unable to open the adapter. %s is not supported by WinPcap\n", + if_name); + LogError(msgBuf); + return false; + } + + LogInfo("ethernet.c: ethernet_init() ok.\n"); + + atexit(ethernet_cleanup); + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns bytes sent success, negative on failure */ +int ethernet_send(BACNET_ADDRESS * dest, /* destination address */ + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len /* number of bytes of data */ + ) +{ + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + /* don't waste time if the socket is not valid */ + if (!ethernet_valid()) { + LogError + ("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + return -1; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = dest->mac[i]; + mtu_len++; + } + } else { + LogError("ethernet.c: invalid destination MAC address!\n"); + return -2; + } + + /* load source ethernet MAC address */ + if (src->mac_len == 6) { + for (i = 0; i < 6; i++) { + mtu[mtu_len] = src->mac[i]; + mtu_len++; + } + } else { + LogError("ethernet.c: invalid source MAC address!\n"); + return -3; + } + if ((14 + 3 + pdu_len) > MAX_MPDU) { + LogError("ethernet.c: PDU is too big to send!\n"); + return -4; + } + /* packet length */ + mtu_len += + encode_unsigned16(&mtu[12], 3 /*DSAP,SSAP,LLC */ + pdu_len); + /* Logical PDU portion */ + mtu[mtu_len++] = 0x82; /* DSAP for BACnet */ + mtu[mtu_len++] = 0x82; /* SSAP for BACnet */ + mtu[mtu_len++] = 0x03; /* Control byte in header */ + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + + /* Send the packet */ + if (pcap_sendpacket(pcap_eth802_fp, mtu, mtu_len) != 0) { + /* did it get sent? */ + char msgBuf[200]; + sprintf(msgBuf, + "ethernet.c: error sending packet: %s\n", + pcap_geterr(pcap_eth802_fp)); + LogError(msgBuf); + return -5; + } + + return mtu_len; +} + +/* function to send a packet out the 802.2 socket */ +/* returns number of bytes sent on success, negative on failure */ +int ethernet_send_pdu(BACNET_ADDRESS * dest, /* destination address */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len /* number of bytes of data */ + ) +{ + int i = 0; /* counter */ + BACNET_ADDRESS src = { 0 }; /* source address */ + + for (i = 0; i < 6; i++) { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + /* function to send a packet out the 802.2 socket */ + /* returns 1 on success, 0 on failure */ + return ethernet_send(dest, /* destination address */ + &src, /* source address */ + pdu, /* any data to be sent - may be null */ + pdu_len /* number of bytes of data */ + ); +} + +/* receives an 802.2 framed packet */ +/* returns the number of octets in the PDU, or zero on failure */ +uint16_t ethernet_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. we ommit it due to winpcap API. */ + ) +{ + struct pcap_pkthdr *header; + int res; + u_char *pkt_data; + uint16_t pdu_len = 0; /* return value */ + + /* Make sure the socket is open */ + if (!ethernet_valid()) { + LogError + ("ethernet.c: invalid 802.2 ethernet interface descriptor!\n"); + return 0; + } + + /* Capture a packet */ + res = pcap_next_ex(pcap_eth802_fp, &header, &pkt_data); + if (res < 0) { + char msgBuf[200]; + sprintf(msgBuf, + "ethernet.c: error in receiving packet: %s\n", + pcap_geterr(pcap_eth802_fp)); + return 0; + } else if (res == 0) + return 0; + + if (header->len == 0 || header->caplen == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((pkt_data[14] != 0x82) && (pkt_data[15] != 0x82)) { + /*eth_log_error("ethernet.c: Non-BACnet packet\n"); */ + return 0; + } + /* copy the source address */ + src->mac_len = 6; + memmove(src->mac, &pkt_data[6], 6); + + /* check destination address for when */ + /* the Ethernet card is in promiscious mode */ + if ((memcmp(&pkt_data[0], Ethernet_MAC_Address, 6) != 0) + && (memcmp(&pkt_data[0], Ethernet_Broadcast, 6) != 0)) { + /*eth_log_error( "ethernet.c: This packet isn't for us\n"); */ + return 0; + } + + (void) decode_unsigned16(&pkt_data[12], &pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + /* copy the buffer into the PDU */ + if (pdu_len < max_pdu) + memmove(&pdu[0], &pkt_data[17], pdu_len); + /* ignore packets that are too large */ + else + pdu_len = 0; + + return pdu_len; +} + +void ethernet_set_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + for (i = 0; i < 6; i++) { + Ethernet_MAC_Address[i] = my_address->mac[i]; + } + + return; +} + +void ethernet_get_my_address(BACNET_ADDRESS * my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void ethernet_get_broadcast_address(BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + for (i = 0; i < 6; i++) { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* denotes broadcast address */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest) +{ + int i = 0; /* counter */ + char msgBuf[200]; + + if (info) { + sprintf(msgBuf, "%s", info); + LogError(msgBuf); + } + /* if */ + if (dest) { + sprintf(msgBuf, + "Address:\n MAC Length=%d\n MAC Address=", dest->mac_len); + LogInfo(msgBuf); + for (i = 0; i < MAX_MAC_LEN; i++) { + sprintf(msgBuf, "%02X ", (unsigned) dest->mac[i]); + LogInfo(msgBuf); + } /* for */ + LogInfo("\n"); + sprintf(msgBuf, + " Net=%hu\n Len=%d\n Adr=", dest->net, dest->len); + LogInfo(msgBuf); + for (i = 0; i < MAX_MAC_LEN; i++) { + sprintf(msgBuf, "%02X ", (unsigned) dest->adr[i]); + LogInfo(msgBuf); + } /* for */ + LogInfo("\n"); + } + /* if ( dest ) */ + return; +} diff --git a/bacnet-stack-0-3-0/ports/win32/main.c b/bacnet-stack-0-3-0/ports/win32/main.c new file mode 100644 index 00000000..d07b98af --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/main.c @@ -0,0 +1,262 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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. +* +*********************************************************************/ + +/* This is one way to use the embedded BACnet stack under Win32 */ +/* compiled with Borland C++ 5.02 or Visual C++ 6.0 */ +#include +#include +#include +#include +#include /* for kbhit and getch */ +#include "iam.h" +#include "address.h" +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "handlers.h" +#include "client.h" +#include "datalink.h" +#include "txbuf.h" + +/* buffer used for receive */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +/* send a whois to see who is on the network */ +static bool Who_Is_Request = true; +bool I_Am_Request = true; + +static void Read_Properties(void) +{ + uint32_t device_id = 0; + bool status = false; + unsigned max_apdu = 0; + BACNET_ADDRESS src; + bool next_device = false; + static unsigned index = 0; + static unsigned property = 0; + /* list of required (and some optional) properties in the + Device Object + note: you could just loop through + all the properties in all the objects. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_CONFORMANCE_CLASS, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing cause + we need to get it with an index method since + the list could be very large */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (address_count()) { + if (address_get_by_index(index, &device_id, &max_apdu, &src)) { + if (object_props[property] < 0) + next_device = true; + else { + status = Send_Read_Property_Request(device_id, /* destination device */ + OBJECT_DEVICE, + device_id, object_props[property], BACNET_ARRAY_ALL); + if (status) + property++; + } + } else + next_device = true; + if (next_device) { + next_device = false; + index++; + if (index >= MAX_ADDRESS_CACHE) + index = 0; + property = 0; + } + } + + return; +} + +static void LocalIAmHandler(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) src; + (void) service_len; + len = iam_decode_service_request(service_request, + &device_id, &max_apdu, &segmentation, &vendor_id); + fprintf(stderr, "Received I-Am Request"); + if (len != -1) { + fprintf(stderr, " from %u!\n", device_id); + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Init_Service_Handlers(void) +{ + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, + handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, + LocalIAmHandler); + + /* 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); + apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, + handler_write_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); +} + +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"); + } +} + +static void print_address_cache(void) +{ + int i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + BACNET_ADDRESS my_address, broadcast_address; + + (void) argc; + (void) argv; + Device_Set_Object_Instance_Number(124); + Init_Service_Handlers(); + /* init the data link layer */ + /* configure standard BACnet/IP port */ + bip_set_port(0xBAC0); + if (!bip_init()) + return 1; + + datalink_get_broadcast_address(&broadcast_address); + print_address("Broadcast", &broadcast_address); + datalink_get_my_address(&my_address); + print_address("Address", &my_address); + + printf("BACnet stack running...\n"); + /* loop forever */ + for (;;) { + /* input */ + + /* returns 0 bytes on timeout */ + pdu_len = bip_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (I_Am_Request) { + I_Am_Request = false; + iam_send(&Handler_Transmit_Buffer[0]); + } else if (Who_Is_Request) { + Who_Is_Request = false; + Send_WhoIs(-1, -1); + } else { + Read_Properties(); + } + + /* output */ + + /* blink LEDs, Turn on or off outputs, etc */ + + /* wait for ESC from keyboard before quitting */ + if (kbhit() && (getch() == 0x1B)) + break; + } + + print_address_cache(); + + return 0; +} diff --git a/bacnet-stack-0-3-0/ports/win32/net.h b/bacnet-stack-0-3-0/ports/win32/net.h new file mode 100644 index 00000000..a717cf7b --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/net.h @@ -0,0 +1,38 @@ +/************************************************************************** +* +* Copyright (C) 2005 Steve Karg +* +* Permission is hereby granted, free of charge, to any 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 NET_H +#define NET_H + +#define WIN32_LEAN_AND_MEAN +#define STRICT 1 + +#include + +#define close closesocket + +typedef int socklen_t; + +#endif diff --git a/bacnet-stack-0-3-0/ports/win32/readme.txt b/bacnet-stack-0-3-0/ports/win32/readme.txt new file mode 100644 index 00000000..a5bd7030 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/readme.txt @@ -0,0 +1,15 @@ +BACnet Stack - Win32 + +This directory contains a demo program that compiles with a Win32 compiler. +It was tested with the freely downloadable Borland C++ 5.5, as well as +Borland C++ 5 and Visual C++ 6.0. + +The makefile.mak file is used with the Borland command line tools. +Run setvars.bat to configure the environment for the Borland tools. +Edit it if necessary to set the correct location of your tools. + +The bacnet.ide file is used with the Borland IDE. + +The bacnet directory is used with Visual C++ 6 tools, and there is a +workspace file bacnet.dsw that is used to compile the demo program. + diff --git a/bacnet-stack-0-3-0/ports/win32/setvars.bat b/bacnet-stack-0-3-0/ports/win32/setvars.bat new file mode 100644 index 00000000..6a804123 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/setvars.bat @@ -0,0 +1,2 @@ +set BORLAND_DIR=\bcc55 + diff --git a/bacnet-stack-0-3-0/ports/win32/stdbool.h b/bacnet-stack-0-3-0/ports/win32/stdbool.h new file mode 100644 index 00000000..2b7511a6 --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ + +#ifndef __cplusplus +typedef int _Bool; +#ifndef bool +#define bool _Bool +#endif +#ifndef true +#define true 1 +#endif +#ifndef false +#define false 0 +#endif +#define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#endif diff --git a/bacnet-stack-0-3-0/ports/win32/stdint.h b/bacnet-stack-0-3-0/ports/win32/stdint.h new file mode 100644 index 00000000..9c003d0a --- /dev/null +++ b/bacnet-stack-0-3-0/ports/win32/stdint.h @@ -0,0 +1,19 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef STDINT_H +#define STDINT_H + +#include + +typedef unsigned char uint8_t; /* 1 byte 0 to 255 */ +typedef signed char int8_t; /* 1 byte -127 to 127 */ +typedef unsigned short uint16_t; /* 2 bytes 0 to 65535 */ +typedef signed short int16_t; /* 2 bytes -32767 to 32767 */ +/*typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215 */ +typedef unsigned long uint32_t; /* 4 bytes 0 to 4294967295 */ +typedef signed long int32_t; /* 4 bytes -2147483647 to 2147483647 */ +/* typedef signed long long int64_t; */ +/* typedef unsigned long long uint64_t; */ + +#endif /* STDINT_H */ diff --git a/bacnet-stack-0-3-0/rd.c b/bacnet-stack-0-3-0/rd.c new file mode 100644 index 00000000..f814471d --- /dev/null +++ b/bacnet-stack-0-3-0/rd.c @@ -0,0 +1,182 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "rd.h" + +/* encode service */ +int rd_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, + BACNET_REINITIALIZED_STATE state, BACNET_CHARACTER_STRING * password) +{ + 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_REINITIALIZE_DEVICE; + apdu_len = 4; + len = encode_context_enumerated(&apdu[apdu_len], 0, state); + 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], 1, + password); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int rd_decode_service_request(uint8_t * apdu, + unsigned apdu_len, + BACNET_REINITIALIZED_STATE * state, BACNET_CHARACTER_STRING * password) +{ + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int value = 0; + + /* check for value pointers */ + if (apdu_len) { + /* Tag 0: reinitializedStateOfDevice */ + if (!decode_is_context_tag(&apdu[len], 0)) + 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 (state) + *state = value; + /* Tag 1: password - optional */ + if (len < apdu_len) { + 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_character_string(&apdu[len], len_value_type, + password); + } + } + + return (int) len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int rd_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_REINITIALIZED_STATE * state, 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_REINITIALIZE_DEVICE) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = rd_decode_service_request(&apdu[offset], + apdu_len - offset, state, password); + } + + return len; +} + +void test_ReinitializeDevice(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_REINITIALIZED_STATE state; + BACNET_REINITIALIZED_STATE test_state; + BACNET_CHARACTER_STRING password; + BACNET_CHARACTER_STRING test_password; + + state = BACNET_REINIT_WARMSTART; + characterstring_init_ansi(&password, "John 3:16"); + len = rd_encode_apdu(&apdu[0], invoke_id, state, &password); + ct_test(pTest, len != 0); + apdu_len = len; + + len = rd_decode_apdu(&apdu[0], + apdu_len, &test_invoke_id, &test_state, &test_password); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_state == state); + ct_test(pTest, characterstring_same(&test_password, &password)); + + return; +} + +#ifdef TEST_REINITIALIZE_DEVICE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReinitializeDevice", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, test_ReinitializeDevice); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_REINITIALIZE_DEVICE */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/rd.cbp b/bacnet-stack-0-3-0/rd.cbp new file mode 100644 index 00000000..6bda3ab8 --- /dev/null +++ b/bacnet-stack-0-3-0/rd.cbp @@ -0,0 +1,57 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/rd.h b/bacnet-stack-0-3-0/rd.h new file mode 100644 index 00000000..ad16a64c --- /dev/null +++ b/bacnet-stack-0-3-0/rd.h @@ -0,0 +1,70 @@ +/*####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 REINITIALIZE_DEVICE_H +#define REINITIALIZE_DEVICE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int rd_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, + BACNET_REINITIALIZED_STATE state, + BACNET_CHARACTER_STRING * password); + +/* decode the service request only */ + int rd_decode_service_request(uint8_t * apdu, + unsigned apdu_len, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password); + +#ifdef TEST +#include "ctest.h" + int rd_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_REINITIALIZED_STATE * state, + BACNET_CHARACTER_STRING * password); + + void test_ReinitializeDevice(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/rd.mak b/bacnet-stack-0-3-0/rd.mak new file mode 100644 index 00000000..8095cccf --- /dev/null +++ b/bacnet-stack-0-3-0/rd.mak @@ -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_REINITIALIZE_DEVICE -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + rd.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = reinitialize_device + +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 diff --git a/bacnet-stack-0-3-0/readme.txt b/bacnet-stack-0-3-0/readme.txt new file mode 100644 index 00000000..04441b03 --- /dev/null +++ b/bacnet-stack-0-3-0/readme.txt @@ -0,0 +1,106 @@ +BACnet open source protocol stack for embedded systems +Version 0.0.2 + +Welcome to the wonderful world of BACnet and true device interoperability! + +About this Project +------------------ + +This BACnet library provides a BACnet application layer, network layer and +media access (MAC) layer communications services for an embedded system. + +BACnet - A Data Communication Protocol for Building Automation and Control +Networks - see bacnet.org. BACnet is a standard data communication protocol for +Building Automation and Control Networks. BACnet is an open protocol, which +means anyone can contribute to the standard, and anyone may use it. The only +caveat is that the BACnet standard document itself is copyrighted by ASHRAE, +and they sell the document to help defray costs of developing and maintaining +the standard (just like IEEE or ANSI or ISO). + +For software developers, the BACnet protocol is a standard way to send and +receive messages on the wire containing data that is understood by other BACnet +compliant devices. The BACnet standard defines a standard way to communicate +over various wires, known as Data Link/Physical Layers: Ethernet, EIA-485, +EIA-232, ARCNET, and LonTalk. The BACnet standard also defines a standard way +to communicate using UDP, IP and HTTP (Web Services). + +This BACnet protocol stack implementation is specifically designed for the +embedded BACnet appliance, using a GPL with exception license (like eCos), +which means that any changes to the core code that are distributed get to come +back into the core code, but the BACnet library can be linked to proprietary +code without the proprietary code becoming GPL. Note that some of the source +files are designed as skeleton or example files, and are not copyrighted. + +The text of the GPL exception included in each source file is as follows: + +"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." + +The code is written in C for portability, and includes unit tests (PC based +unit tests). Since the code is designed to be portable, it compiles with GCC as +well as other compilers, such as Borland C++ or MicroChip C18. + +The BACnet protocol is an ASHRAE/ANSI/ISO standard, so this library adheres to +that standard. BACnet has no royalties or licensing restrictions, and +registration for a BACnet vendor ID is free. + +What the code does +------------------ + +The stack comes with unit tests that can be run in a command shell using the +test.sh script. The unit tests can also be run using individual .mak files. +They were tested on a Linux PC. + +The BACnet stack was functionally tested using VTS (Visual Test Shell), another +project hosted on SourceForge, as well as various controllers and workstations. +Using the Makefile in the project root directory, a sample application is +created that runs under Linux. It uses the BACnet Ethernet physical layer for +communication. It requires root priveleges to run the 802.2 Ethernet interface. + +$ make clean all +$ sudo ./bacnet + +The BACnet stack currently supports the following services marked +with an X, and hopefully will support the rest of the services listed +in the future. + Initiate Execute + -------- ------- +Who Is X +I Am X +Read Property X +Read Property Multiple +Write Property +Write Property Multiple +Device Communication Control +ReinitializeDevice +Time Synchronization +UTC Time Synchronization +Atomic Read File +Atomic Write File +Subscribe COV +Confirmed COV Notification +Unconfirmed COV Notification +Get Alarm Summary +Get Event Information +Acknowledge Alarm +Confirmed Event Notification +Unconfirmed Event Notification +Who Has +I Have + +The BACnet stack currently implements a Device Object, and handles most of the +Read Property inquiries for the required Device Object properties. The stack +handles Who-Is inquiries with an I-Am, and handles reject messages for services +not currently supported. + +If you want to help this project, join the developers mailing list at: +http://lists.sourceforge.net/mailman/listinfo/bacnet-developers + +I hope that you get your BACnet Device working! + +Steve Karg +skarg@users.sourceforge.net diff --git a/bacnet-stack-0-3-0/reject.c b/bacnet-stack-0-3-0/reject.c new file mode 100644 index 00000000..8edc441d --- /dev/null +++ b/bacnet-stack-0-3-0/reject.c @@ -0,0 +1,168 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" + +/* encode service */ +int reject_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, uint8_t reject_reason) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_REJECT; + apdu[1] = invoke_id; + apdu[2] = reject_reason; + apdu_len = 3; + } + + return apdu_len; +} + +/* decode the service request only */ +int reject_decode_service_request(uint8_t * apdu, + unsigned apdu_len, uint8_t * invoke_id, uint8_t * reject_reason) +{ + int len = 0; + + if (apdu_len) { + if (invoke_id) + *invoke_id = apdu[0]; + if (reject_reason) + *reject_reason = apdu[1]; + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* decode the whole APDU - mainly used for unit testing */ +int reject_decode_apdu(uint8_t * apdu, + unsigned apdu_len, uint8_t * invoke_id, uint8_t * reject_reason) +{ + 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_REJECT) + return -1; + if (apdu_len > 1) { + len = reject_decode_service_request(&apdu[1], + apdu_len - 1, invoke_id, reject_reason); + } + } + + return len; +} + +void testReject(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 0; + uint8_t reject_reason = 0; + uint8_t test_invoke_id = 0; + uint8_t test_reject_reason = 0; + + len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason); + ct_test(pTest, len != 0); + apdu_len = len; + + len = reject_decode_apdu(&apdu[0], + apdu_len, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_reject_reason == reject_reason); + + /* change type to get negative response */ + apdu[0] = PDU_TYPE_ABORT; + len = reject_decode_apdu(&apdu[0], + apdu_len, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len == -1); + + /* test NULL APDU */ + len = reject_decode_apdu(NULL, + apdu_len, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len == -1); + + /* force a zero length */ + len = reject_decode_apdu(&apdu[0], + 0, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len == 0); + + + /* check them all... */ + for (invoke_id = 0; invoke_id < 255; invoke_id++) { + for (reject_reason = 0; reject_reason < 255; reject_reason++) { + len = reject_encode_apdu(&apdu[0], invoke_id, reject_reason); + apdu_len = len; + ct_test(pTest, len != 0); + len = reject_decode_apdu(&apdu[0], + apdu_len, &test_invoke_id, &test_reject_reason); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, test_reject_reason == reject_reason); + } + } +} + +#ifdef TEST_REJECT +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Reject", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReject); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_REJECT */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/reject.h b/bacnet-stack-0-3-0/reject.h new file mode 100644 index 00000000..83d11d1d --- /dev/null +++ b/bacnet-stack-0-3-0/reject.h @@ -0,0 +1,60 @@ +/*####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 REJECT_H +#define REJECT_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + int reject_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, uint8_t reject_reason); + + int reject_decode_service_request(uint8_t * apdu, + unsigned apdu_len, uint8_t * invoke_id, uint8_t * reject_reason); + +#ifdef TEST + int reject_decode_apdu(uint8_t * apdu, + unsigned apdu_len, uint8_t * invoke_id, uint8_t * reject_reason); + + void testReject(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/reject.mak b/bacnet-stack-0-3-0/reject.mak new file mode 100644 index 00000000..a59f6daf --- /dev/null +++ b/bacnet-stack-0-3-0/reject.mak @@ -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_REJECT -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + reject.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = reject + +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 diff --git a/bacnet-stack-0-3-0/ringbuf.c b/bacnet-stack-0-3-0/ringbuf.c new file mode 100644 index 00000000..5ebe12af --- /dev/null +++ b/bacnet-stack-0-3-0/ringbuf.c @@ -0,0 +1,273 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 by 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####*/ + +/* Functional Description: Generic ring buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#include +#include +#include "ringbuf.h" + +/**************************************************************************** +* DESCRIPTION: Returns the empty/full status of the ring buffer +* RETURN: true if the ring buffer is empty, false if it is not. +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Empty(RING_BUFFER const *b) +{ + return (b->count == 0); +} + +/**************************************************************************** +* DESCRIPTION: Looks at the data from the head of the list without removing it +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +char *Ringbuf_Get_Front(RING_BUFFER const *b) +{ + return (b->count ? &(b->data[b->head * b->element_size]) : NULL); +} + +/**************************************************************************** +* DESCRIPTION: Gets the data from the front of the list, and removes it +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +char *Ringbuf_Pop_Front(RING_BUFFER * b) +{ + char *data = NULL; /* return value */ + + if (b->count) { + data = &(b->data[b->head * b->element_size]); + b->head++; + if (b->head >= b->element_count) + b->head = 0; + b->count--; + } + + return data; +} + +/**************************************************************************** +* DESCRIPTION: Adds an element of data to the ring buffer +* RETURN: true on succesful add, false if not added +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool Ringbuf_Put(RING_BUFFER * b, /* ring buffer structure */ + char *data_element) +{ /* one element to add to the ring */ + bool status = false; /* return value */ + unsigned offset = 0; /* offset into array of data */ + char *ring_data = NULL; /* used to help point ring data */ + unsigned i; /* loop counter */ + + if (b && data_element) { + /* limit the amount of data that we accept */ + if (b->count < b->element_count) { + offset = b->head + b->count; + if (offset >= b->element_count) + offset -= b->element_count; + ring_data = b->data + offset * b->element_size; + for (i = 0; i < b->element_size; i++) { + ring_data[i] = data_element[i]; + } + b->count++; + status = true; + } + } + + return status; +} + +/**************************************************************************** +* DESCRIPTION: Configures the ring buffer +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void Ringbuf_Init(RING_BUFFER * b, /* ring buffer structure */ + char *data, /* data block or array of data */ + unsigned element_size, /* size of one element in the data block */ + unsigned element_count) +{ /* number of elements in the data block */ + b->head = 0; + b->count = 0; + b->data = data; + b->element_size = element_size; + b->element_count = element_count; + + return; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +/* test the FIFO */ +#define RING_BUFFER_DATA_SIZE 5 +#define RING_BUFFER_COUNT 16 +void testRingBuf(Test * pTest) +{ + RING_BUFFER test_buffer; + char data_store[RING_BUFFER_DATA_SIZE * RING_BUFFER_COUNT]; + char data[RING_BUFFER_DATA_SIZE]; + char *test_data; + unsigned index; + unsigned data_index; + unsigned count; + unsigned dummy; + bool status; + + Ringbuf_Init(&test_buffer, data_store, RING_BUFFER_DATA_SIZE, + RING_BUFFER_COUNT); + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) { + data[data_index] = data_index; + } + status = Ringbuf_Put(&test_buffer, data); + ct_test(pTest, status == true); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + + test_data = Ringbuf_Get_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) { + ct_test(pTest, test_data[data_index] == data[data_index]); + } + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + + test_data = Ringbuf_Pop_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++) { + ct_test(pTest, test_data[data_index] == data[data_index]); + } + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + /* fill to max */ + for (index = 0; index < RING_BUFFER_COUNT; index++) { + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + data[data_index] = index; + } + status = Ringbuf_Put(&test_buffer, data); + ct_test(pTest, status == true); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + } + /* verify actions on full buffer */ + for (index = 0; index < RING_BUFFER_COUNT; index++) { + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + data[data_index] = index; + } + status = Ringbuf_Put(&test_buffer, data); + ct_test(pTest, status == false); + ct_test(pTest, !Ringbuf_Empty(&test_buffer)); + } + + /* check buffer full */ + for (index = 0; index < RING_BUFFER_COUNT; index++) { + test_data = Ringbuf_Get_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + ct_test(pTest, test_data[data_index] == index); + } + + test_data = Ringbuf_Pop_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + ct_test(pTest, test_data[data_index] == index); + } + } + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + /* test the ring around the buffer */ + for (index = 0; index < RING_BUFFER_COUNT; index++) { + for (count = 1; count < 4; count++) { + dummy = index * count; + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + data[data_index] = dummy; + } + status = Ringbuf_Put(&test_buffer, data); + ct_test(pTest, status == true); + } + + for (count = 1; count < 4; count++) { + dummy = index * count; + test_data = Ringbuf_Get_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + ct_test(pTest, test_data[data_index] == dummy); + } + + test_data = Ringbuf_Pop_Front(&test_buffer); + for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; + data_index++) { + ct_test(pTest, test_data[data_index] == dummy); + } + } + } + ct_test(pTest, Ringbuf_Empty(&test_buffer)); + + + return; +} + +#ifdef TEST_RINGBUF +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("ringbuf", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testRingBuf); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif +#endif diff --git a/bacnet-stack-0-3-0/ringbuf.h b/bacnet-stack-0-3-0/ringbuf.h new file mode 100644 index 00000000..6a6e4f72 --- /dev/null +++ b/bacnet-stack-0-3-0/ringbuf.h @@ -0,0 +1,71 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2004 by 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####*/ + +/* Functional Description: Generic ring buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#ifndef RINGBUF_H +#define RINGBUF_H + +#include +#include + +struct ring_buffer_t { + char *data; /* block of memory or array of data */ + unsigned element_size; /* how many bytes for each chunk */ + unsigned element_count; /* number of chunks of data */ + unsigned head; /* first chunk of data */ + unsigned count; /* number of chunks in use */ +}; +typedef struct ring_buffer_t RING_BUFFER; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool Ringbuf_Empty(RING_BUFFER const *b); + char *Ringbuf_Get_Front(RING_BUFFER const *b); + char *Ringbuf_Pop_Front(RING_BUFFER * b); + bool Ringbuf_Put(RING_BUFFER * b, /* ring buffer structure */ + char *data_element); /* one element to add to the ring */ + void Ringbuf_Init(RING_BUFFER * b, /* ring buffer structure */ + char *data, /* data block or array of data */ + unsigned element_size, /* size of one element in the data block */ + unsigned element_count); /* number of elements in the data block */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/ringbuf.mak b/bacnet-stack-0-3-0/ringbuf.mak new file mode 100644 index 00000000..26c685c4 --- /dev/null +++ b/bacnet-stack-0-3-0/ringbuf.mak @@ -0,0 +1,29 @@ +#Makefile to build ringbuf tests +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -g +CFLAGS = -Wall -I. -Itest -DTEST -DTEST_RINGBUF -g + +OBJS = ringbuf.o test/ctest.o + +TARGET = ringbuf + +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 + diff --git a/bacnet-stack-0-3-0/rp.c b/bacnet-stack-0-3-0/rp.c new file mode 100644 index 00000000..d26613d3 --- /dev/null +++ b/bacnet-stack-0-3-0/rp.c @@ -0,0 +1,341 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "rp.h" + +/* encode service */ +int rp_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_READ_PROPERTY_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */ + apdu_len = 4; + len = encode_context_object_id(&apdu[apdu_len], 0, + data->object_type, data->object_instance); + apdu_len += len; + len = encode_context_enumerated(&apdu[apdu_len], 1, + data->object_property); + apdu_len += len; + /* optional array index */ + if (data->array_index != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->array_index); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int rp_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_READ_PROPERTY_DATA * data) +{ + unsigned len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int type = 0; /* for decoding */ + int property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu_len && data) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, &data->object_instance); + data->object_type = type; + /* Tag 1: Property ID */ + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + data->object_property = property; + /* Tag 2: Optional Array Index */ + if (len < apdu_len) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 2) { + len += decode_unsigned(&apdu[len], len_value_type, + &array_value); + data->array_index = array_value; + } else + data->array_index = BACNET_ARRAY_ALL; + } else + data->array_index = BACNET_ARRAY_ALL; + } + + return (int) len; +} + +int rp_ack_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_READ_PROPERTY_DATA * data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_PROPERTY; /* service choice */ + apdu_len = 3; + /* service ack follows */ + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, + data->object_type, data->object_instance); + apdu_len += encode_context_enumerated(&apdu[apdu_len], 1, + data->object_property); + /* context 2 array index is optional */ + if (data->array_index != BACNET_ARRAY_ALL) { + apdu_len += encode_context_unsigned(&apdu[apdu_len], 2, + data->array_index); + } + /* propertyValue */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 3); + for (len = 0; len < data->application_data_len; len++) { + apdu[apdu_len++] = data->application_data[len]; + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + } + + return apdu_len; +} + +int rp_ack_decode_service_request(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + BACNET_READ_PROPERTY_DATA * data) +{ + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int tag_len = 0; /* length of tag decode */ + int len = 0; /* total length of decodes */ + int object = 0, property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* FIXME: check apdu_len against the len during decode */ + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[0], 0)) + return -1; + len = 1; + len += decode_object_id(&apdu[len], &object, &data->object_instance); + data->object_type = object; + /* Tag 1: Property ID */ + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + data->object_property = property; + /* Tag 2: Optional Array Index */ + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, &array_value); + data->array_index = array_value; + } else + data->array_index = BACNET_ARRAY_ALL; + /* Tag 3: opening context tag */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + /* a tag number of 3 is not extended so only one octet */ + len++; + /* don't decode the application tag number or its data here */ + data->application_data = &apdu[len]; + data->application_data_len = apdu_len - len - 1 /*closing tag */ ; + } else + return -1; + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int rp_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, BACNET_READ_PROPERTY_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_READ_PROPERTY) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = rp_decode_service_request(&apdu[offset], + apdu_len - offset, data); + } + + return len; +} + +int rp_ack_decode_apdu(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, BACNET_READ_PROPERTY_DATA * data) +{ + int len = 0; + int 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]; + if (apdu[2] != SERVICE_CONFIRMED_READ_PROPERTY) + return -1; + offset = 3; + if (apdu_len > offset) { + len = rp_ack_decode_service_request(&apdu[offset], + apdu_len - offset, data); + } + + return len; +} + +void testReadPropertyAck(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + uint8_t apdu2[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 1; + uint8_t test_invoke_id = 0; + BACNET_READ_PROPERTY_DATA data; + BACNET_READ_PROPERTY_DATA test_data; + BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; + uint32_t object_instance = 0; + int object = 0; + + data.object_type = OBJECT_DEVICE; + data.object_instance = 1; + data.object_property = PROP_OBJECT_IDENTIFIER; + data.array_index = BACNET_ARRAY_ALL; + + data.application_data_len = encode_bacnet_object_id(&apdu2[0], + data.object_type, data.object_instance); + data.application_data = &apdu2[0]; + + len = rp_ack_encode_apdu(&apdu[0], invoke_id, &data); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = rp_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */ + &test_invoke_id, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + + 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.object_property == data.object_property); + ct_test(pTest, test_data.array_index == data.array_index); + ct_test(pTest, + test_data.application_data_len == data.application_data_len); + + /* since object property == object_id, decode the application data using + the appropriate decode function */ + len = decode_object_id(test_data.application_data, + &object, &object_instance); + object_type = object; + ct_test(pTest, object_type == data.object_type); + ct_test(pTest, object_instance == data.object_instance); +} + +void testReadProperty(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + BACNET_READ_PROPERTY_DATA data; + BACNET_READ_PROPERTY_DATA test_data; + + data.object_type = OBJECT_DEVICE; + data.object_instance = 1; + data.object_property = PROP_OBJECT_IDENTIFIER; + data.array_index = BACNET_ARRAY_ALL; + len = rp_encode_apdu(&apdu[0], invoke_id, &data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = rp_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.object_property == data.object_property); + ct_test(pTest, test_data.array_index == data.array_index); + + return; +} + +#ifdef TEST_READ_PROPERTY +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReadProperty", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReadProperty); + assert(rc); + rc = ct_addTestFunction(pTest, testReadPropertyAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_READ_PROPERTY */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/rp.h b/bacnet-stack-0-3-0/rp.h new file mode 100644 index 00000000..4332ab18 --- /dev/null +++ b/bacnet-stack-0-3-0/rp.h @@ -0,0 +1,84 @@ +/*####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 READPROPERTY_H +#define READPROPERTY_H + +#include +#include + +typedef struct BACnet_Read_Property_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + int32_t array_index; + uint8_t *application_data; + int application_data_len; +} BACNET_READ_PROPERTY_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int rp_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_READ_PROPERTY_DATA * data); + +/* decode the service request only */ + int rp_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_READ_PROPERTY_DATA * data); + + int rp_ack_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_READ_PROPERTY_DATA * data); + + int rp_ack_decode_service_request(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + BACNET_READ_PROPERTY_DATA * data); + + +#ifdef TEST +#include "ctest.h" + int rp_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, BACNET_READ_PROPERTY_DATA * data); + + int rp_ack_decode_apdu(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, BACNET_READ_PROPERTY_DATA * data); + + void test_ReadProperty(Test * pTest); + void test_ReadPropertyAck(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/rp.mak b/bacnet-stack-0-3-0/rp.mak new file mode 100644 index 00000000..5a336e46 --- /dev/null +++ b/bacnet-stack-0-3-0/rp.mak @@ -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_READ_PROPERTY -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + rp.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = readproperty + +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 diff --git a/bacnet-stack-0-3-0/rpm.c b/bacnet-stack-0-3-0/rpm.c new file mode 100644 index 00000000..08b49ac9 --- /dev/null +++ b/bacnet-stack-0-3-0/rpm.c @@ -0,0 +1,744 @@ +/*####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 +#include "bacenum.h" +#include "bacerror.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "rpm.h" + +/* encode the initial portion of the service */ +int rpm_encode_apdu_init(uint8_t * apdu, uint8_t invoke_id) +{ + 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_READ_PROPERTY_MULTIPLE; /* service choice */ + apdu_len = 4; + } + + return apdu_len; +} + +int rpm_encode_apdu_object_begin(uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, uint32_t object_instance) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_context_object_id(&apdu[0], 0, + object_type, object_instance); + /* Tag 1: sequence of ReadAccessSpecification */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + } + + return apdu_len; +} + +int rpm_encode_apdu_object_property(uint8_t * apdu, + BACNET_PROPERTY_ID object_property, int32_t array_index) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_context_enumerated(&apdu[0], 0, object_property); + /* optional array index */ + if (array_index != BACNET_ARRAY_ALL) + apdu_len += encode_context_unsigned(&apdu[apdu_len], 1, + array_index); + } + + return apdu_len; +} + +int rpm_encode_apdu_object_end(uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 1); + } + + return apdu_len; +} + +/* decode the object portion of the service request only */ +int rpm_decode_object_id(uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, uint32_t * object_instance) +{ + unsigned len = 0; + int type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu && apdu_len && object_type && object_instance) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, object_instance); + if (object_type) + *object_type = type; + /* Tag 1: sequence of ReadAccessSpecification */ + if (!decode_is_opening_tag_number(&apdu[len], 1)) + return -1; + len++; /* opening tag is only one octet */ + } + + return (int) len; +} + +int rpm_decode_object_end(uint8_t * apdu, unsigned apdu_len) +{ + int len = 0; /* total length of the apdu, return value */ + + if (apdu && apdu_len) { + if (decode_is_closing_tag_number(apdu, 1)) + len = 1; + } + + return len; +} + +/* decode the object property portion of the service request only */ +int rpm_decode_object_property(uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, int32_t * array_index) +{ + unsigned len = 0; + unsigned option_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for valid pointers */ + if (apdu && apdu_len && object_property && array_index) { + /* Tag 0: propertyIdentifier */ + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number != 0) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + if (object_property) + *object_property = property; + /* Tag 1: Optional propertyArrayIndex */ + if (len < apdu_len) { + option_len = + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 2) { + len += option_len; + len += decode_unsigned(&apdu[len], len_value_type, + &array_value); + *array_index = array_value; + } else + *array_index = BACNET_ARRAY_ALL; + } else + *array_index = BACNET_ARRAY_ALL; + } + + return (int) len; +} + +int rpm_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t ** service_request, unsigned *service_request_len) +{ + 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_READ_PROPERTY_MULTIPLE) + return -1; + offset = 4; + + if (apdu_len > offset) { + if (service_request) + *service_request = &apdu[offset]; + if (service_request_len) + *service_request_len = apdu_len - offset; + } + + return offset; +} + +int rpm_ack_encode_apdu_init(uint8_t * apdu, uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE; /* service choice */ + apdu_len = 3; + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_begin(uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, uint32_t object_instance) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 0: objectIdentifier */ + apdu_len = encode_context_object_id(&apdu[0], 0, + object_type, object_instance); + /* Tag 1: listOfResults */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property(uint8_t * apdu, + BACNET_PROPERTY_ID object_property, int32_t array_index) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 2: propertyIdentifier */ + apdu_len = encode_context_enumerated(&apdu[0], 2, object_property); + /* Tag 3: optional propertyArrayIndex */ + if (array_index != BACNET_ARRAY_ALL) + apdu_len += encode_context_unsigned(&apdu[apdu_len], 3, + array_index); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property_value(uint8_t * apdu, + uint8_t * application_data, unsigned application_data_len) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + unsigned len = 0; + + if (apdu) { + /* Tag 4: propertyValue */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 4); + for (len = 0; len < application_data_len; len++) { + apdu[apdu_len++] = application_data[len]; + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 4); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_property_error(uint8_t * apdu, + BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* Tag 5: propertyAccessError */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 5); + apdu_len += encode_tagged_enumerated(&apdu[apdu_len], error_class); + apdu_len += encode_tagged_enumerated(&apdu[apdu_len], error_code); + apdu_len += encode_closing_tag(&apdu[apdu_len], 5); + } + + return apdu_len; +} + +int rpm_ack_encode_apdu_object_end(uint8_t * apdu) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len = encode_closing_tag(&apdu[0], 1); + } + + return apdu_len; +} + +/* decode the object portion of the service request only */ +int rpm_ack_decode_object_id(uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, uint32_t * object_instance) +{ + unsigned len = 0; + int type = 0; /* for decoding */ + + /* check for value pointers */ + if (apdu && apdu_len && object_type && object_instance) { + /* Tag 0: objectIdentifier */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, object_instance); + if (object_type) + *object_type = type; + /* Tag 1: listOfResults */ + if (!decode_is_opening_tag_number(&apdu[len], 1)) + return -1; + len++; /* opening tag is only one octet */ + } + + return (int) len; +} + +/* is this the end of the list of this objects properties values? */ +int rpm_ack_decode_object_end(uint8_t * apdu, unsigned apdu_len) +{ + int len = 0; /* total length of the apdu, return value */ + + if (apdu && apdu_len) { + if (decode_is_closing_tag_number(apdu, 1)) + len = 1; + } + + return len; +} + +int rpm_ack_decode_object_property(uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, int32_t * array_index) +{ + unsigned len = 0; + unsigned tag_len = 0; + uint8_t tag_number = 0; + uint32_t len_value_type = 0; + int property = 0; /* for decoding */ + uint32_t array_value = 0; /* for decoding */ + + /* check for valid pointers */ + if (apdu && apdu_len && object_property && array_index) { + /* Tag 2: propertyIdentifier */ + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number != 2) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + if (object_property) + *object_property = property; + /* Tag 3: Optional propertyArrayIndex */ + tag_len = decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value_type); + if (tag_number == 3) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, + &array_value); + *array_index = array_value; + } else + *array_index = BACNET_ARRAY_ALL; + } + + return (int) len; +} + +int rpm_ack_decode_apdu(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + uint8_t ** service_request, unsigned *service_request_len) +{ + int 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]; + if (apdu[2] != SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE) + return -1; + offset = 3; + if (apdu_len > offset) { + if (service_request) + *service_request = &apdu[offset]; + if (service_request_len) + *service_request_len = apdu_len - offset; + } + + return offset; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +void testReadPropertyMultiple(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int test_len = 0; + int apdu_len = 0; + uint8_t invoke_id = 12; + uint8_t test_invoke_id = 0; + uint8_t *service_request = NULL; + unsigned service_request_len = 0; + BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; + uint32_t object_instance = 0; + BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER; + int32_t array_index = 0; + + /* build the RPM - try to make it easy for the Application Layer development */ + /* IDEA: similar construction, but pass apdu, apdu_len pointer, size of apdu to + let the called function handle the out of space problem that these get into + by returning a boolean of success/failure. + It almost needs to use the keylist library or something similar. + Also check case of storing a backoff point (i.e. save enough room for object_end) */ + apdu_len = rpm_encode_apdu_init(&apdu[0], invoke_id); + /* each object has a beginning and an end */ + apdu_len += rpm_encode_apdu_object_begin(&apdu[apdu_len], + OBJECT_DEVICE, 123); + /* then stuff as many properties into it as APDU length will allow */ + apdu_len += rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_NAME, BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]); + /* each object has a beginning and an end */ + apdu_len += rpm_encode_apdu_object_begin(&apdu[apdu_len], + OBJECT_ANALOG_INPUT, 33); + apdu_len += rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_property(&apdu[apdu_len], + PROP_ALL, BACNET_ARRAY_ALL); + apdu_len += rpm_encode_apdu_object_end(&apdu[apdu_len]); + + ct_test(pTest, apdu_len != 0); + + test_len = rpm_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */ + &service_request_len); + ct_test(pTest, test_len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, service_request != NULL); + ct_test(pTest, service_request_len > 0); + + test_len = rpm_decode_object_id(service_request, + service_request_len, &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_DEVICE); + ct_test(pTest, object_instance == 123); + len = test_len; + /* decode the object property portion of the service request */ + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_OBJECT_NAME); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* try again - we should fail */ + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* is it the end of this object? */ + test_len = rpm_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* try to decode an object id */ + test_len = rpm_decode_object_id(&service_request[len], + service_request_len - len, &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_ANALOG_INPUT); + ct_test(pTest, object_instance == 33); + len += test_len; + /* decode the object property portion of the service request only */ + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_ALL); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + test_len = rpm_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* got an error -1, is it the end of this object? */ + test_len = rpm_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + ct_test(pTest, len == service_request_len); +} + +void testReadPropertyMultipleAck(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int test_len = 0; + int apdu_len = 0; + uint8_t invoke_id = 12; + uint8_t test_invoke_id = 0; + uint8_t *service_request = NULL; + unsigned service_request_len = 0; + BACNET_OBJECT_TYPE object_type = OBJECT_DEVICE; + uint32_t object_instance = 0; + BACNET_PROPERTY_ID object_property = PROP_OBJECT_IDENTIFIER; + int32_t array_index = 0; + BACNET_APPLICATION_DATA_VALUE application_data[4] = { {0} }; + BACNET_APPLICATION_DATA_VALUE test_application_data = { 0 }; + uint8_t application_data_buffer[MAX_APDU] = { 0 }; + int application_data_buffer_len = 0; + BACNET_ERROR_CLASS error_class; + BACNET_ERROR_CODE error_code; + + /* build the RPM - try to make it easy for the + Application Layer development */ + /* IDEA: similar construction, but pass apdu, apdu_len pointer, + size of apdu to let the called function handle the out of + space problem that these get into by returning a boolean + of success/failure. + It almost needs to use the keylist library or something similar. + Also check case of storing a backoff point + (i.e. save enough room for object_end) */ + apdu_len = rpm_ack_encode_apdu_init(&apdu[0], invoke_id); + + /* object beginning */ + apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], + OBJECT_DEVICE, 123); + /* reply property */ + apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_IDENTIFIER, BACNET_ARRAY_ALL); + /* reply value */ + application_data[0].tag = BACNET_APPLICATION_TAG_OBJECT_ID; + application_data[0].type.Object_Id.type = OBJECT_DEVICE; + application_data[0].type.Object_Id.instance = 123; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[0]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* reply property */ + apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_OBJECT_TYPE, BACNET_ARRAY_ALL); + /* reply value */ + application_data[1].tag = BACNET_APPLICATION_TAG_ENUMERATED; + application_data[1].type.Enumerated = OBJECT_DEVICE; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[1]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* object end */ + apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); + + /* object beginning */ + apdu_len += rpm_ack_encode_apdu_object_begin(&apdu[apdu_len], + OBJECT_ANALOG_INPUT, 33); + /* reply property */ + apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_PRESENT_VALUE, BACNET_ARRAY_ALL); + /* reply value */ + application_data[2].tag = BACNET_APPLICATION_TAG_REAL; + application_data[2].type.Real = 0.0; + application_data_buffer_len = + bacapp_encode_application_data(&application_data_buffer[0], + &application_data[2]); + apdu_len += + rpm_ack_encode_apdu_object_property_value(&apdu[apdu_len], + &application_data_buffer[0], application_data_buffer_len); + /* reply property */ + apdu_len += rpm_ack_encode_apdu_object_property(&apdu[apdu_len], + PROP_DEADBAND, BACNET_ARRAY_ALL); + /* reply error */ + apdu_len += rpm_ack_encode_apdu_object_property_error(&apdu[apdu_len], + ERROR_CLASS_PROPERTY, ERROR_CODE_UNKNOWN_PROPERTY); + /* object end */ + apdu_len += rpm_ack_encode_apdu_object_end(&apdu[apdu_len]); + ct_test(pTest, apdu_len != 0); + + /****** decode the packet ******/ + test_len = rpm_ack_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, &service_request, /* will point to the service request in the apdu */ + &service_request_len); + ct_test(pTest, test_len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + ct_test(pTest, service_request != NULL); + ct_test(pTest, service_request_len > 0); + /* the first part should be the first object id */ + test_len = rpm_ack_decode_object_id(service_request, + service_request_len, &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_DEVICE); + ct_test(pTest, object_instance == 123); + len = test_len; + /* extract the property */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, object_property == PROP_OBJECT_IDENTIFIER); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result? An error or a value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + /* note: if this was an array, there could have been + more than one element to decode */ + test_len = bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[0], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_OBJECT_TYPE); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + test_len = bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[1], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + /* this time we should fail */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* see if it is the end of this object */ + test_len = rpm_ack_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* try to decode another object id */ + test_len = rpm_ack_decode_object_id(&service_request[len], + service_request_len - len, &object_type, &object_instance); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_type == OBJECT_ANALOG_INPUT); + ct_test(pTest, object_instance == 33); + len += test_len; + /* decode the object property portion of the service request only */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_PRESENT_VALUE); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 4)); + len++; + /* decode the object property portion of the service request */ + test_len = bacapp_decode_application_data(&service_request[len], + service_request_len - len, &test_application_data); + ct_test(pTest, test_len > 0); + ct_test(pTest, bacapp_same_value(&application_data[2], + &test_application_data)); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 4)); + len++; + /* see if there is another property */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len != -1); + ct_test(pTest, object_property == PROP_DEADBAND); + ct_test(pTest, array_index == BACNET_ARRAY_ALL); + len += test_len; + /* what is the result value? */ + ct_test(pTest, decode_is_opening_tag_number(&service_request[len], 5)); + len++; + /* it was an error reply */ + test_len = bacerror_decode_error_class_and_code(&service_request[len], + service_request_len - len, &error_class, &error_code); + ct_test(pTest, test_len != 0); + ct_test(pTest, error_class == ERROR_CLASS_PROPERTY); + ct_test(pTest, error_code == ERROR_CODE_UNKNOWN_PROPERTY); + len += test_len; + ct_test(pTest, decode_is_closing_tag_number(&service_request[len], 5)); + len++; + /* is there another property? */ + test_len = rpm_ack_decode_object_property(&service_request[len], + service_request_len - len, &object_property, &array_index); + ct_test(pTest, test_len == -1); + /* got an error -1, is it the end of this object? */ + test_len = rpm_ack_decode_object_end(&service_request[len], + service_request_len - len); + ct_test(pTest, test_len == 1); + len += test_len; + /* check for another object */ + test_len = rpm_ack_decode_object_id(&service_request[len], + service_request_len - len, &object_type, &object_instance); + ct_test(pTest, test_len == 0); + ct_test(pTest, len == service_request_len); +} + +#ifdef TEST_READ_PROPERTY_MULTIPLE +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet ReadPropertyMultiple", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testReadPropertyMultiple); + assert(rc); + rc = ct_addTestFunction(pTest, testReadPropertyMultipleAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_READ_PROPERTY_MULTIPLE */ + +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/rpm.h b/bacnet-stack-0-3-0/rpm.h new file mode 100644 index 00000000..e868657d --- /dev/null +++ b/bacnet-stack-0-3-0/rpm.h @@ -0,0 +1,119 @@ +/*####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 RPM_H +#define RPM_H + +#include +#include +#include "bacenum.h" +#include "bacdef.h" +#include "bacapp.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode functions */ +/* Start with the Init function, and then add an object, + then add its properties, and then end the object. + Continue to add objects and properties as needed + until the APDU is full.*/ + +/* RPM */ + int rpm_encode_apdu_init(uint8_t * apdu, uint8_t invoke_id); + + int rpm_encode_apdu_object_begin(uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, uint32_t object_instance); + + int rpm_encode_apdu_object_property(uint8_t * apdu, + BACNET_PROPERTY_ID object_property, int32_t array_index); + + int rpm_encode_apdu_object_end(uint8_t * apdu); + + int rpm_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + uint8_t ** service_request, unsigned *service_request_len); + +/* decode the object portion of the service request only */ + int rpm_decode_object_id(uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, uint32_t * object_instance); + +/* is this the end of this object property list? */ + int rpm_decode_object_end(uint8_t * apdu, unsigned apdu_len); + +/* decode the object property portion of the service request only */ + int rpm_decode_object_property(uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, int32_t * array_index); + +/* RPM Ack */ + int rpm_ack_encode_apdu_object_begin(uint8_t * apdu, + BACNET_OBJECT_TYPE object_type, uint32_t object_instance); + + int rpm_ack_encode_apdu_object_property_value(uint8_t * apdu, + uint8_t * application_data, unsigned application_data_len); + + int rpm_ack_encode_apdu_object_property_error(uint8_t * apdu, + BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code); + + int rpm_ack_encode_apdu_object_end(uint8_t * apdu); + + int rpm_ack_decode_object_id(uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_TYPE * object_type, uint32_t * object_instance); +/* is this the end of the list of this objects properties values? */ + int rpm_ack_decode_object_end(uint8_t * apdu, unsigned apdu_len); + int rpm_ack_decode_object_property(uint8_t * apdu, + unsigned apdu_len, + BACNET_PROPERTY_ID * object_property, int32_t * array_index); +/* decode the object property value portion of the service request only */ + int rpm_ack_decode_object_property_value(uint8_t * apdu, + unsigned apdu_len, + uint8_t ** application_data, unsigned *application_data_len); + int rpm_ack_decode_apdu(uint8_t * apdu, int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + uint8_t ** service_request, unsigned *service_request_len); + +#ifdef TEST +#include "ctest.h" + void testReadPropertyMultiple(Test * pTest); + void testReadPropertyMultipleAck(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/rpm.mak b/bacnet-stack-0-3-0/rpm.mak new file mode 100644 index 00000000..3051d5b1 --- /dev/null +++ b/bacnet-stack-0-3-0/rpm.mak @@ -0,0 +1,38 @@ +#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_READ_PROPERTY_MULTIPLE -g + +SRCS = bacdcode.c \ + bacerror.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + bacstr.c \ + datetime.c \ + rpm.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = rpm + +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 diff --git a/bacnet-stack-0-3-0/sbuf.c b/bacnet-stack-0-3-0/sbuf.c new file mode 100644 index 00000000..cbb5d954 --- /dev/null +++ b/bacnet-stack-0-3-0/sbuf.c @@ -0,0 +1,209 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 by 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####*/ + +/* Functional Description: Static buffer library for deeply + embedded system. See the unit tests for usage examples. */ +#include +#include +#include +#include "sbuf.h" + +void sbuf_init(STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* actual size, in bytes, of the data block or array of data */ + unsigned size) +{ /* number of bytes used */ + if (b) { + b->data = data; + b->size = size; + b->count = 0; + } + + return; +} + +/* returns true if count==0, false if count > 0 */ +bool sbuf_empty(STATIC_BUFFER const *b) +{ + return (b ? (b->count == 0) : false); +} + +char *sbuf_data(STATIC_BUFFER const *b) +{ + return (b ? b->data : NULL); +} + +unsigned sbuf_size(STATIC_BUFFER * b) +{ + return (b ? b->size : 0); +} + +unsigned sbuf_count(STATIC_BUFFER * b) +{ + return (b ? b->count : 0); +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_put(STATIC_BUFFER * b, /* static buffer structure */ + unsigned offset, /* where to start */ + char *data, /* number of bytes used */ + unsigned data_size) +{ /* how many to add */ + bool status = false; /* return value */ + + if (b && b->data) { + if (((offset + data_size) < b->size)) { + b->count = offset + data_size; + while (data_size) { + b->data[offset] = *data; + offset++; + data++; + data_size--; + } + status = true; + } + } + + return status; +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_append(STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* number of bytes used */ + unsigned data_size) +{ /* how many to add */ + unsigned count = 0; + + if (b) + count = b->count; + + return sbuf_put(b, count, data, data_size); +} + +/* returns true if successful, false if not enough room to append data */ +bool sbuf_truncate(STATIC_BUFFER * b, /* static buffer structure */ + unsigned count) +{ /* total number of bytes in use */ + bool status = false; /* return value */ + + if (b) { + if (count < b->size) { + b->count = count; + status = true; + } + } + + return status; +} + +#ifdef TEST +#include +#include + +#include "ctest.h" + +void testStaticBuffer(Test * pTest) +{ + STATIC_BUFFER sbuffer; + char *data1 = "Joshua"; + char *data2 = "Anna"; + char *data3 = "Christopher"; + char *data4 = "Mary"; + char data_buffer[480] = ""; + char test_data_buffer[480] = ""; + char *data; + unsigned count; + + sbuf_init(&sbuffer, NULL, 0); + ct_test(pTest, sbuf_empty(&sbuffer) == true); + ct_test(pTest, sbuf_data(&sbuffer) == NULL); + ct_test(pTest, sbuf_size(&sbuffer) == 0); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == false); + + sbuf_init(&sbuffer, data_buffer, sizeof(data_buffer)); + ct_test(pTest, sbuf_empty(&sbuffer) == true); + ct_test(pTest, sbuf_data(&sbuffer) == data_buffer); + ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer)); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + + ct_test(pTest, sbuf_append(&sbuffer, data1, strlen(data1)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data2, strlen(data2)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data3, strlen(data3)) == true); + ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true); + strcat(test_data_buffer, data1); + strcat(test_data_buffer, data2); + strcat(test_data_buffer, data3); + strcat(test_data_buffer, data4); + ct_test(pTest, sbuf_count(&sbuffer) == strlen(test_data_buffer)); + + data = sbuf_data(&sbuffer); + count = sbuf_count(&sbuffer); + ct_test(pTest, memcmp(data, test_data_buffer, count) == 0); + ct_test(pTest, count == strlen(test_data_buffer)); + + ct_test(pTest, sbuf_truncate(&sbuffer, 0) == true); + ct_test(pTest, sbuf_count(&sbuffer) == 0); + ct_test(pTest, sbuf_size(&sbuffer) == sizeof(data_buffer)); + ct_test(pTest, sbuf_append(&sbuffer, data4, strlen(data4)) == true); + data = sbuf_data(&sbuffer); + count = sbuf_count(&sbuffer); + ct_test(pTest, memcmp(data, data4, count) == 0); + ct_test(pTest, count == strlen(data4)); + + return; +} + +#ifdef TEST_STATIC_BUFFER +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("static buffer", NULL); + + /* individual tests */ + rc = ct_addTestFunction(pTest, testStaticBuffer); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_STATIC_BUFFER */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/sbuf.h b/bacnet-stack-0-3-0/sbuf.h new file mode 100644 index 00000000..ffcad78a --- /dev/null +++ b/bacnet-stack-0-3-0/sbuf.h @@ -0,0 +1,80 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 by 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####*/ + +/* Functional Description: Static buffer library for deeply + embedded system. See the unit tests for usage examples. */ + +#ifndef SBUF_H +#define SBUF_H + +#include +#include + +struct static_buffer_t { + char *data; /* block of memory or array of data */ + unsigned size; /* actual size, in bytes, of the block of data */ + unsigned count; /* number of bytes in use */ +}; +typedef struct static_buffer_t STATIC_BUFFER; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void sbuf_init(STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* actual size, in bytes, of the data block or array of data */ + unsigned size); /* number of bytes used */ +/* returns true if size==0, false if size > 0 */ + bool sbuf_empty(STATIC_BUFFER const *b); + char *sbuf_data(STATIC_BUFFER const *b); + unsigned sbuf_size(STATIC_BUFFER * b); + unsigned sbuf_count(STATIC_BUFFER * b); +/* returns true if successful, false if not enough room to append data */ + bool sbuf_put(STATIC_BUFFER * b, /* static buffer structure */ + unsigned offset, /* where to start */ + char *data, /* number of bytes used */ + unsigned data_size); /* how many to add */ +/* returns true if successful, false if not enough room to append data */ + bool sbuf_append(STATIC_BUFFER * b, /* static buffer structure */ + char *data, /* number of bytes used */ + unsigned data_size); /* how many to add */ +/* returns true if successful, false if not enough room to append data */ + bool sbuf_truncate(STATIC_BUFFER * b, /* static buffer structure */ + unsigned count); /* total number of bytes in use */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/sbuf.mak b/bacnet-stack-0-3-0/sbuf.mak new file mode 100644 index 00000000..385bd457 --- /dev/null +++ b/bacnet-stack-0-3-0/sbuf.mak @@ -0,0 +1,32 @@ +#Makefile to build ringbuf tests +CC = gcc +BASEDIR = . +#CFLAGS = -Wall -I. +# -g for debugging with gdb +#CFLAGS = -Wall -I. -g +CFLAGS = -Wall -I. -Itest -DTEST -DTEST_STATIC_BUFFER -g + +SRCS = sbuf.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = sbuf + +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 + diff --git a/bacnet-stack-0-3-0/svn2cl.xsl b/bacnet-stack-0-3-0/svn2cl.xsl new file mode 100644 index 00000000..f4226b5c --- /dev/null +++ b/bacnet-stack-0-3-0/svn2cl.xsl @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bacnet-stack-0-3-0/test/ctest.c b/bacnet-stack-0-3-0/test/ctest.c new file mode 100644 index 00000000..94739b79 --- /dev/null +++ b/bacnet-stack-0-3-0/test/ctest.c @@ -0,0 +1,169 @@ +/* ctest.c: Implements the CTest Framework */ + +#include "ctest.h" +#include +#include +#include +#include + +/* Number of tests to hold incrementally */ +enum { CHUNK = 10 }; + +Test *ct_create(const char *name, void (*init) (Test *)) +{ + int backOutLevel = 0; + Test *pTest = malloc(sizeof(Test)); + if (pTest) { + pTest->nPass = pTest->nFail = pTest->nTests = 0; + pTest->pStream = stdout; + + /* Allocate array of fptrs: */ + assert(CHUNK); + pTest->pTestFuns = calloc(CHUNK, sizeof(TestFunc)); + if (pTest->pTestFuns) { + pTest->maxTests = CHUNK; + /* Allocate test name: */ + assert(name); + pTest->name = malloc(strlen(name) + 1); + if (pTest->name) + strcpy(pTest->name, name); + else + ++backOutLevel; + } else + ++backOutLevel; + } + + /* Back-out allocations if memory failed: */ + if (backOutLevel) { + switch (backOutLevel) { + case 2: + free(pTest->pTestFuns); + pTest->pTestFuns = NULL; + case 1: + free(pTest); + pTest = NULL; + } + } else if (init) { + assert(pTest); + init(pTest); + } + return pTest; +} + +void ct_destroy(Test * pTest) +{ + assert(pTest); + assert(pTest->pTestFuns); + free(pTest->pTestFuns); + pTest->pTestFuns = NULL; + assert(pTest->name); + free(pTest->name); + pTest->name = NULL; + free(pTest); +} + +bool ct_addTestFunction(Test * pTest, TestFunc tfun) +{ + assert(pTest); + assert(pTest->pTestFuns); + if (pTest->nTests == pTest->maxTests) { + size_t newSize = pTest->nTests + CHUNK; + TestFunc *new_pTestFuns = realloc(pTest->pTestFuns, + newSize * sizeof(TestFunc)); + if (!new_pTestFuns) + return false; + pTest->pTestFuns = new_pTestFuns; + pTest->maxTests += CHUNK; + } + assert(pTest->nTests < pTest->maxTests); + pTest->pTestFuns[pTest->nTests++] = tfun; + return true; +} + +void ct_setStream(Test * pTest, FILE * pStream) +{ + pTest->pStream = pStream; +} + +FILE *ct_getStream(Test * pTest) +{ + return pTest->pStream; +} + +long ct_report(Test * pTest) +{ + assert(pTest); + if (pTest->pStream) { + fprintf(pTest->pStream, + "Test \"%s\":\n\tPassed: %ld\n\tFailed: %ld\n", + pTest->name, pTest->nPass, pTest->nFail); + } + return pTest->nFail; +} + + +void ct_succeed(Test * pTest) +{ + assert(pTest); + ++pTest->nPass; +} + +void ct_do_test(Test * pTest, const char *str, + bool cond, const char *file, long line) +{ + assert(pTest); + if (!cond) + ct_do_fail(pTest, str, file, line); + else + ct_succeed(pTest); +} + +void ct_do_fail(Test * pTest, const char *str, const char *file, long line) +{ + assert(pTest); + ++pTest->nFail; + if (pTest->pStream) { + fprintf(pTest->pStream, + "%s failure: (%s), %s (line %ld)\n", + pTest->name, str, file, line); + } +} + +long ct_getNumPassed(Test * pTest) +{ + assert(pTest); + return pTest->nPass; +} + +long ct_getNumFailed(Test * pTest) +{ + assert(pTest); + return pTest->nFail; +} + +long ct_run(Test * pTest) +{ + size_t testNum; + assert(pTest); + for (testNum = 0; testNum < pTest->nTests; ++testNum) + pTest->pTestFuns[testNum] (pTest); + return pTest->nFail; +} + +void ct_reset(Test * pTest) +{ + assert(pTest); + pTest->nFail = pTest->nPass = 0; +} + +const char *ct_getName(Test * pTest) +{ + assert(pTest); + return (pTest->name); +} + +long ct_getNumTests(Test * pTest) +{ + assert(pTest); + return pTest->nTests; +} diff --git a/bacnet-stack-0-3-0/test/ctest.h b/bacnet-stack-0-3-0/test/ctest.h new file mode 100644 index 00000000..9be0bbe6 --- /dev/null +++ b/bacnet-stack-0-3-0/test/ctest.h @@ -0,0 +1,59 @@ +/* ctest.h + * + * Defines a test framework for C projects. + */ +#ifndef CTEST_H +#define CTEST_H + +#include +#include + +#define ct_test(test, cond) \ + ct_do_test(test, #cond, cond, __FILE__, __LINE__) +#define ct_fail(test, str) \ + ct_do_fail(test, str, __FILE__, __LINE__) + +typedef struct _Test Test; + +typedef void (*TestFunc) (Test *); + +struct _Test { + char *name; + FILE *pStream; + size_t nTests; + size_t maxTests; + TestFunc *pTestFuns; + long nPass; + long nFail; +}; + +#ifdef __cplusplus +extern "C" { +#endif + + Test *ct_create(const char *name, void (*init) (Test *)); + void ct_destroy(Test * pTest); + + const char *ct_getName(Test * pTest); + long ct_getNumPassed(Test * pTest); + long ct_getNumFailed(Test * pTest); + long ct_getNumTests(Test * pTest); + FILE *ct_getStream(Test * pTest); + void ct_setStream(Test * pTest, FILE * stream); + + bool ct_addTestFunction(Test * pTest, TestFunc tfun); + void ct_succeed(Test * pTest); + long ct_run(Test * pTest); + long ct_report(Test * pTest); + void ct_reset(Test * pTest); + +/* Not intended for end-users: */ + void ct_do_test(Test * pTest, const char *str, + bool cond, const char *file, long line); + void ct_do_fail(Test * pTest, const char *str, + const char *file, long line); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/bacnet-stack-0-3-0/timesync.c b/bacnet-stack-0-3-0/timesync.c new file mode 100644 index 00000000..77839d1d --- /dev/null +++ b/bacnet-stack-0-3-0/timesync.c @@ -0,0 +1,214 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "bacapp.h" +#include "timesync.h" + +/* encode service */ +int timesync_encode_apdu_service(uint8_t * apdu, + BACNET_UNCONFIRMED_SERVICE service, + BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu && my_date && my_time) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = service; + apdu_len = 2; + len = encode_tagged_date(&apdu[apdu_len], my_date); + apdu_len += len; + len = encode_tagged_time(&apdu[apdu_len], my_time); + apdu_len += len; + } + + return apdu_len; +} + +int timesync_utc_encode_apdu(uint8_t * apdu, + BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + return timesync_encode_apdu_service(apdu, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, my_date, my_time); +} + +int timesync_encode_apdu(uint8_t * apdu, + BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + return timesync_encode_apdu_service(apdu, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, my_date, my_time); +} + +/* decode the service request only */ +int timesync_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + + if (apdu_len && my_date && my_time) { + /* date */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number == BACNET_APPLICATION_TAG_DATE) { + len += decode_date(&apdu[len], my_date); + } else + return -1; + /* time */ + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number == BACNET_APPLICATION_TAG_TIME) { + len += decode_bacnet_time(&apdu[len], my_time); + } else + return -1; + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int timesync_decode_apdu_service(uint8_t * apdu, + BACNET_UNCONFIRMED_SERVICE service, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + int len = 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 -1; + if (apdu[1] != service) + return -1; + /* optional limits - must be used as a pair */ + if (apdu_len > 2) { + len = timesync_decode_service_request(&apdu[2], apdu_len - 2, + my_date, my_time); + } + + return len; +} + +int timesync_utc_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + return timesync_decode_apdu_service(apdu, + SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION, + apdu_len, my_date, my_time); +} + +int timesync_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + return timesync_decode_apdu_service(apdu, + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, + apdu_len, my_date, my_time); +} + +void testTimeSyncData(Test * pTest, + BACNET_DATE * my_date, BACNET_TIME * my_time) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_DATE test_date; + BACNET_TIME test_time; + + len = timesync_encode_apdu(&apdu[0], my_date, my_time); + ct_test(pTest, len != 0); + apdu_len = len; + len = timesync_decode_apdu(&apdu[0], apdu_len, &test_date, &test_time); + ct_test(pTest, len != -1); + ct_test(pTest, bacapp_same_time(my_time, &test_time)); + ct_test(pTest, bacapp_same_date(my_date, &test_date)); + + len = timesync_utc_encode_apdu(&apdu[0], my_date, my_time); + ct_test(pTest, len != 0); + apdu_len = len; + len = + timesync_utc_decode_apdu(&apdu[0], apdu_len, &test_date, + &test_time); + ct_test(pTest, len != -1); + ct_test(pTest, bacapp_same_time(my_time, &test_time)); + ct_test(pTest, bacapp_same_date(my_date, &test_date)); +} + +void testTimeSync(Test * pTest) +{ + BACNET_DATE bdate; + BACNET_TIME btime; + + bdate.year = 2006; /* AD */ + bdate.month = 4; /* 1=Jan */ + bdate.day = 11; /* 1..31 */ + bdate.wday = 1; /* 1=Monday */ + + btime.hour = 7; + btime.min = 0; + btime.sec = 3; + btime.hundredths = 1; + + testTimeSyncData(pTest, &bdate, &btime); +} + +#ifdef TEST_TIMESYNC +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Time-Sync", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTimeSync); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/timesync.cbp b/bacnet-stack-0-3-0/timesync.cbp new file mode 100644 index 00000000..4f7df523 --- /dev/null +++ b/bacnet-stack-0-3-0/timesync.cbp @@ -0,0 +1,126 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/timesync.h b/bacnet-stack-0-3-0/timesync.h new file mode 100644 index 00000000..f54a3c26 --- /dev/null +++ b/bacnet-stack-0-3-0/timesync.h @@ -0,0 +1,66 @@ +/*####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 TIMESYNC_H +#define TIMESYNC_H + +#include +#include +#include "bacdef.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int timesync_utc_encode_apdu(uint8_t * apdu, + BACNET_DATE * my_date, BACNET_TIME * my_time); + int timesync_encode_apdu(uint8_t * apdu, + BACNET_DATE * my_date, BACNET_TIME * my_time); +/* decode the service request only */ + int timesync_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time); + int timesync_utc_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time); + int timesync_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_DATE * my_date, BACNET_TIME * my_time); + +#ifdef TEST +#include "ctest.h" + void testTimeSync(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/timesync.mak b/bacnet-stack-0-3-0/timesync.mak new file mode 100644 index 00000000..ee6adc95 --- /dev/null +++ b/bacnet-stack-0-3-0/timesync.mak @@ -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 -DTEST -DTEST_TIMESYNC -g + +SRCS = bacapp.c \ + bacdcode.c \ + bacstr.c \ + bactext.c \ + bigend.c \ + indtext.c \ + timesync.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = timesync + +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 diff --git a/bacnet-stack-0-3-0/tsm.c b/bacnet-stack-0-3-0/tsm.c new file mode 100644 index 00000000..434d1cdf --- /dev/null +++ b/bacnet-stack-0-3-0/tsm.c @@ -0,0 +1,343 @@ +/*####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 +#include +#include +#include /* memmove() */ +#include "bits.h" +#include "apdu.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "tsm.h" +#include "config.h" +#include "device.h" +#include "datalink.h" +#include "handlers.h" +#include "address.h" + +/* Transaction State Machine */ +/* Really only needed for segmented messages */ +/* and a little for sending confirmed messages */ +/* If we are only a server and only initiate broadcasts, */ +/* then we don't need a TSM layer. */ + +/* FIXME: not coded for segmentation */ + +/* declare space for the TSM transactions, and set it up in the init. */ +/* table rules: an Invoke ID = 0 is an unused spot in the table */ +static BACNET_TSM_DATA TSM_List[MAX_TSM_TRANSACTIONS]; + +/* returns MAX_TSM_TRANSACTIONS if not found */ +static uint8_t tsm_find_invokeID_index(uint8_t invokeID) +{ + unsigned i = 0; /* counter */ + uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == invokeID) { + index = i; + break; + } + } + + return index; +} + +static uint8_t tsm_find_first_free_index(void) +{ + unsigned i = 0; /* counter */ + uint8_t index = MAX_TSM_TRANSACTIONS; /* return value */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == 0) { + index = i; + break; + } + } + + return index; +} + +bool tsm_transaction_available(void) +{ + bool status = false; /* return value */ + unsigned i = 0; /* counter */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].InvokeID == 0) { + /* one is available! */ + status = true; + break; + } + } + + return status; +} + +uint8_t tsm_transaction_idle_count(void) +{ + uint8_t count = 0; /* return value */ + unsigned i = 0; /* counter */ + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if ((TSM_List[i].InvokeID == 0) && + (TSM_List[i].state == TSM_STATE_IDLE)) { + /* one is available! */ + count++; + } + } + + return count; +} + +/* gets the next free invokeID, + and reserves a spot in the table + returns 0 if none are available */ +uint8_t tsm_next_free_invokeID(void) +{ + static uint8_t current_invokeID = 1; /* incremented... */ + uint8_t index = 0; + uint8_t invokeID = 0; + bool found = false; + + /* is there even space available? */ + if (tsm_transaction_available()) { + while (!found) { + index = tsm_find_invokeID_index(current_invokeID); + /* not found - that is good! */ + if (index == MAX_TSM_TRANSACTIONS) { + found = true; + /* set this id into the table */ + index = tsm_find_first_free_index(); + if (index != MAX_TSM_TRANSACTIONS) { + TSM_List[index].InvokeID = invokeID = current_invokeID; + TSM_List[index].state = TSM_STATE_IDLE; + TSM_List[index].RequestTimer = Device_APDU_Timeout(); + /* update for the next call or check */ + current_invokeID++; + /* skip zero - we treat that internally as invalid or no free */ + if (current_invokeID == 0) + current_invokeID = 1; + } + } + } + } + + return invokeID; +} + +void tsm_set_confirmed_unsegmented_transaction(uint8_t invokeID, + BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, uint16_t apdu_len) +{ + uint16_t j = 0; + uint8_t index; + + if (invokeID) { + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + /* assign the transaction */ + TSM_List[index].state = TSM_STATE_AWAIT_CONFIRMATION; + TSM_List[index].RetryCount = Device_Number_Of_APDU_Retries(); + /* start the timer */ + TSM_List[index].RequestTimer = Device_APDU_Timeout(); + /* copy the data */ + for (j = 0; j < apdu_len; j++) { + TSM_List[index].apdu[j] = apdu[j]; + } + TSM_List[index].apdu_len = apdu_len; + npdu_copy_data(&TSM_List[index].npdu_data, ndpu_data); + address_copy(&TSM_List[index].dest, dest); + } + } + + return; +} + +/* used to retrieve the transaction payload */ +/* if we wanted to find out what we sent (i.e. when we get an ack) */ +bool tsm_get_transaction_pdu(uint8_t invokeID, + BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, uint16_t * apdu_len) +{ + uint16_t j = 0; + uint8_t index; + bool found = false; + + if (invokeID) { + index = tsm_find_invokeID_index(invokeID); + /* how much checking is needed? state? dest match? just invokeID? */ + if (index < MAX_TSM_TRANSACTIONS) { + /* FIXME: we may want to free the transaction so it doesn't timeout */ + /* retrieve the transaction */ + /* FIXME: bounds check the pdu_len? */ + *apdu_len = TSM_List[index].apdu_len; + for (j = 0; j < *apdu_len; j++) { + apdu[j] = TSM_List[index].apdu[j]; + } + npdu_copy_data(ndpu_data, &TSM_List[index].npdu_data); + address_copy(dest, &TSM_List[index].dest); + found = true; + } + } + + return found; +} + +/* called once a millisecond or slower */ +void tsm_timer_milliseconds(uint16_t milliseconds) +{ + unsigned i = 0; /* counter */ + int bytes_sent = 0; + + for (i = 0; i < MAX_TSM_TRANSACTIONS; i++) { + if (TSM_List[i].state == TSM_STATE_AWAIT_CONFIRMATION) { + if (TSM_List[i].RequestTimer > milliseconds) + TSM_List[i].RequestTimer -= milliseconds; + else + TSM_List[i].RequestTimer = 0; + /* timeout. retry? */ + if (TSM_List[i].RequestTimer == 0) { + TSM_List[i].RetryCount--; + TSM_List[i].RequestTimer = Device_APDU_Timeout(); + if (TSM_List[i].RetryCount) { + bytes_sent = datalink_send_pdu(&TSM_List[i].dest, + &TSM_List[i].npdu_data, + &TSM_List[i].apdu[0], TSM_List[i].apdu_len); + } else { + /* note: the invoke id has not been cleared yet + and this indicates a failed message: + IDLE and a valid invoke id */ + TSM_List[i].state = TSM_STATE_IDLE; + } + } + } + } +} + +void tsm_free_invoke_id(uint8_t invokeID) +{ + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + TSM_List[index].state = TSM_STATE_IDLE; + TSM_List[index].InvokeID = 0; + } +} + +/* see if the invoke ID has been made free */ +bool tsm_invoke_id_free(uint8_t invokeID) +{ + bool status = true; + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) + status = false; + + return status; +} + +/* see if the invoke ID has failed get a confirmation */ +bool tsm_invoke_id_failed(uint8_t invokeID) +{ + bool status = false; + uint8_t index; + + index = tsm_find_invokeID_index(invokeID); + if (index < MAX_TSM_TRANSACTIONS) { + /* a valid invoke ID and the state is IDLE is a + message that failed to confirm */ + if (TSM_List[index].state == TSM_STATE_IDLE) + status = true; + } + + return status; +} + + +#ifdef TEST +#include +#include +#include "ctest.h" + +/* flag to send an I-Am */ +bool I_Am_Request = true; + +/* 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; +} + +void testTSM(Test * pTest) +{ + /* FIXME: add some unit testing... */ + return; +} + +#ifdef TEST_TSM +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet TSM", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTSM); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_TSM */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/tsm.h b/bacnet-stack-0-3-0/tsm.h new file mode 100644 index 00000000..bd6e81ed --- /dev/null +++ b/bacnet-stack-0-3-0/tsm.h @@ -0,0 +1,124 @@ +/*####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 TSM_H +#define TSM_H + +#include +#include +#include +#include "bacdef.h" +#include "npdu.h" + +/* note: TSM functionality is optional - only needed if we are + doing client requests */ +#if TSM_ENABLED +typedef enum { + TSM_STATE_IDLE, + TSM_STATE_AWAIT_CONFIRMATION, + TSM_STATE_AWAIT_RESPONSE, + TSM_STATE_SEGMENTED_REQUEST, + TSM_STATE_SEGMENTED_CONFIRMATION +} BACNET_TSM_STATE; + +/* 5.4.1 Variables And Parameters */ +/* The following variables are defined for each instance of */ +/* Transaction State Machine: */ +typedef struct BACnet_TSM_Data { + /* used to count APDU retries */ + uint8_t RetryCount; + /* used to count segment retries */ + /*uint8_t SegmentRetryCount; */ + /* used to control APDU retries and the acceptance of server replies */ + /*bool SentAllSegments; */ + /* stores the sequence number of the last segment received in order */ + /*uint8_t LastSequenceNumber; */ + /* stores the sequence number of the first segment of */ + /* a sequence of segments that fill a window */ + /*uint8_t InitialSequenceNumber; */ + /* stores the current window size */ + /*uint8_t ActualWindowSize; */ + /* stores the window size proposed by the segment sender */ + /*uint8_t ProposedWindowSize; */ + /* used to perform timeout on PDU segments */ + /*uint8_t SegmentTimer; */ + /* used to perform timeout on Confirmed Requests */ + /* in milliseconds */ + uint16_t RequestTimer; + /* unique id */ + uint8_t InvokeID; + /* state that the TSM is in */ + BACNET_TSM_STATE state; + /* the address we sent it to */ + BACNET_ADDRESS dest; + /* the network layer info */ + BACNET_NPDU_DATA npdu_data; + /* copy of the APDU, should we need to send it again */ + uint8_t apdu[MAX_PDU]; + unsigned apdu_len; +} BACNET_TSM_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + bool tsm_transaction_available(void); + uint8_t tsm_transaction_idle_count(void); + void tsm_timer_milliseconds(uint16_t milliseconds); +/* free the invoke ID when the reply comes back */ + void tsm_free_invoke_id(uint8_t invokeID); +/* use these in tandem */ + uint8_t tsm_next_free_invokeID(void); +/* returns the same invoke ID that was given */ + void tsm_set_confirmed_unsegmented_transaction(uint8_t invokeID, + BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, uint16_t apdu_len); +/* returns true if transaction is found */ + bool tsm_get_transaction_pdu(uint8_t invokeID, + BACNET_ADDRESS * dest, BACNET_NPDU_DATA * ndpu_data, + uint8_t * apdu, uint16_t * apdu_len); + + bool tsm_invoke_id_free(uint8_t invokeID); + bool tsm_invoke_id_failed(uint8_t invokeID); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/* define out any functions necessary for compile */ +#else + +#define tsm_free_invoke_id(x) (void)x; + +#endif + +#endif diff --git a/bacnet-stack-0-3-0/tsm.mak b/bacnet-stack-0-3-0/tsm.mak new file mode 100644 index 00000000..398fb1f4 --- /dev/null +++ b/bacnet-stack-0-3-0/tsm.mak @@ -0,0 +1,51 @@ +#Makefile to build test case +CC = gcc +# -g for debugging with gdb +DEFINES = -DTSM_ENABLED=1 -DTEST -DTEST_TSM -DBIG_ENDIAN=0 -DBACDL_TEST=1 +INCLUDES = -I. -Idemo/object -Idemo/handler -Itest -Iports/linux +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = address.c \ + bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.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/lsp.c \ + demo/object/mso.c \ + demo/object/lc.c \ + iam.c \ + dcc.c \ + npdu.c \ + apdu.c \ + tsm.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = tsm + +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 diff --git a/bacnet-stack-0-3-0/unittest.sh b/bacnet-stack-0-3-0/unittest.sh new file mode 100755 index 00000000..a6a400f9 --- /dev/null +++ b/bacnet-stack-0-3-0/unittest.sh @@ -0,0 +1,190 @@ +#!/bin/sh +# Unit tests builder / runner for this project + +rm test.log +touch test.log + +make -f abort.mak clean +make -f abort.mak +./abort >> test.log +make -f abort.mak clean + +make -f address.mak clean +make -f address.mak +./address >> test.log +make -f address.mak clean + +make -f cov.mak clean +make -f cov.mak +./cov >> test.log +make -f cov.mak clean + +make -f dcc.mak clean +make -f dcc.mak +./dcc >> test.log +make -f dcc.mak clean + +make -f demo/object/ai.mak clean +make -f demo/object/ai.mak +./analog_input >> test.log +make -f demo/object/ai.mak clean + +make -f demo/object/ao.mak clean +make -f demo/object/ao.mak +./analog_output >> test.log +make -f demo/object/ao.mak clean + +make -f demo/object/av.mak clean +make -f demo/object/av.mak +./analog_value >> test.log +make -f demo/object/av.mak clean + +make -f arf.mak clean +make -f arf.mak +./atomicreadfile >> test.log +make -f arf.mak clean + +make -f awf.mak clean +make -f awf.mak +./atomicwritefile >> test.log +make -f awf.mak clean + +make -f bacapp.mak clean +make -f bacapp.mak +./bacapp >> test.log +make -f bacapp.mak clean + +make -f bacdcode.mak clean +make -f bacdcode.mak +./bacdcode >> test.log +make -f bacdcode.mak clean + +make -f bacerror.mak clean +make -f bacerror.mak +./bacerror >> test.log +make -f bacerror.mak clean + +make -f bacstr.mak clean +make -f bacstr.mak +./bacstr >> test.log +make -f bacstr.mak clean + +make -f demo/object/bi.mak clean +make -f demo/object/bi.mak +./binary_input >> test.log +make -f demo/object/bi.mak clean + +make -f demo/object/bo.mak clean +make -f demo/object/bo.mak +./binary_output >> test.log +make -f demo/object/bo.mak clean + +make -f demo/object/bv.mak clean +make -f demo/object/bv.mak +./binary_value >> test.log +make -f demo/object/bv.mak clean + +make -f crc.mak clean +make -f crc.mak +./crc >> test.log +make -f crc.mak clean + +make -f datetime.mak clean +make -f datetime.mak +./datetime >> test.log +make -f datetime.mak clean + +make -f demo/object/device.mak clean +make -f demo/object/device.mak +./device >> test.log +make -f demo/object/device.mak clean + +make -f iam.mak clean +make -f iam.mak +./iam >> test.log +make -f iam.mak clean + +make -f ihave.mak clean +make -f ihave.mak +./ihave >> test.log +make -f ihave.mak clean + +make -f indtext.mak clean +make -f indtext.mak +./indtext >> test.log +make -f indtext.mak clean + +make -f demo/object/lc.mak clean +make -f demo/object/lc.mak +./loadcontrol >> test.log +make -f demo/object/lc.mak clean + +make -f demo/object/lsp.mak clean +make -f demo/object/lsp.mak +./lsp >> test.log +make -f demo/object/lsp.mak clean + +make -f demo/object/mso.mak clean +make -f demo/object/mso.mak +./multistate_output >> test.log +make -f demo/object/mso.mak clean + +make -f mstp.mak clean +make -f mstp.mak +./mstp >> test.log +make -f mstp.mak clean + +make -f npdu.mak clean +make -f npdu.mak +./npdu >> test.log +make -f npdu.mak clean + +make -f rd.mak clean +make -f rd.mak +./reinitialize_device >> test.log +make -f rd.mak clean + +make -f reject.mak clean +make -f reject.mak +./reject >> test.log +make -f reject.mak clean + +make -f ringbuf.mak clean +make -f ringbuf.mak +./ringbuf >> test.log +make -f ringbuf.mak clean + +make -f rp.mak clean +make -f rp.mak +./readproperty >> test.log +make -f rp.mak clean + +make -f rpm.mak clean +make -f rpm.mak +./rpm >> test.log +make -f rpm.mak clean + +make -f sbuf.mak clean +make -f sbuf.mak +./sbuf >> test.log +make -f sbuf.mak clean + +make -f tsm.mak clean +make -f tsm.mak +./tsm >> test.log +make -f tsm.mak clean + +make -f whois.mak clean +make -f whois.mak +./whois >> test.log +make -f whois.mak clean + +make -f whohas.mak clean +make -f whohas.mak +./whohas >> test.log +make -f whohas.mak clean + +make -f wp.mak clean +make -f wp.mak +./writeproperty >> test.log +make -f wp.mak clean diff --git a/bacnet-stack-0-3-0/whohas.c b/bacnet-stack-0-3-0/whohas.c new file mode 100644 index 00000000..928b80a5 --- /dev/null +++ b/bacnet-stack-0-3-0/whohas.c @@ -0,0 +1,248 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "whohas.h" + +/* encode service - use -1 for limit for unlimited */ + +int whohas_encode_apdu(uint8_t * apdu, BACNET_WHO_HAS_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_WHO_HAS; /* service choice */ + apdu_len = 2; + /* optional limits - must be used as a pair */ + if ((data->low_limit >= 0) + && (data->low_limit <= BACNET_MAX_INSTANCE) + && (data->high_limit >= 0) + && (data->high_limit <= BACNET_MAX_INSTANCE)) { + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->low_limit); + apdu_len += len; + len = encode_context_unsigned(&apdu[apdu_len], + 1, data->high_limit); + apdu_len += len; + } + if (data->object_name) { + len = encode_context_character_string(&apdu[apdu_len], + 3, &data->object.name); + apdu_len += len; + } else { + len = encode_context_object_id(&apdu[apdu_len], + 2, + data->object.identifier.type, + data->object.identifier.instance); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int whohas_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_WHO_HAS_DATA * data) +{ + int len = 0; + 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) { + /* optional limits - must be used as a pair */ + 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); + if (decoded_value <= BACNET_MAX_INSTANCE) + data->low_limit = decoded_value; + if (!decode_is_context_tag(&apdu[len], 1)) + return -1; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) + data->high_limit = decoded_value; + } else { + data->low_limit = -1; + data->high_limit = -1; + } + /* object id */ + if (decode_is_context_tag(&apdu[len], 2)) { + data->object_name = false; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_object_id(&apdu[len], &decoded_type, + &data->object.identifier.instance); + data->object.identifier.type = decoded_type; + } + /* object name */ + else if (decode_is_context_tag(&apdu[len], 3)) { + data->object_name = true; + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += + decode_character_string(&apdu[len], len_value, + &data->object.name); + } + /* missing required parameters */ + else + return -1; + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int whohas_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_WHO_HAS_DATA * data) +{ + int len = 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 -1; + if (apdu[1] != SERVICE_UNCONFIRMED_WHO_HAS) + return -1; + /* optional limits - must be used as a pair */ + if (apdu_len > 2) { + len = whohas_decode_service_request(&apdu[2], apdu_len - 2, data); + } + + return len; +} + +void testWhoHasData(Test * pTest, BACNET_WHO_HAS_DATA * data) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + BACNET_WHO_HAS_DATA test_data; + + len = whohas_encode_apdu(&apdu[0], data); + ct_test(pTest, len != 0); + apdu_len = len; + + len = whohas_decode_apdu(&apdu[0], apdu_len, &test_data); + ct_test(pTest, len != -1); + ct_test(pTest, test_data.low_limit == data->low_limit); + ct_test(pTest, test_data.high_limit == data->high_limit); + ct_test(pTest, test_data.object_name == data->object_name); + /* Object ID */ + if (data->object_name == false) { + ct_test(pTest, test_data.object.identifier.type == + data->object.identifier.type); + ct_test(pTest, test_data.object.identifier.instance == + data->object.identifier.instance); + } + /* Object Name */ + else { + ct_test(pTest, characterstring_same(&test_data.object.name, + &data->object.name)); + } +} + +void testWhoHas(Test * pTest) +{ + BACNET_WHO_HAS_DATA data; + + data.low_limit = -1; + data.high_limit = -1; + data.object_name = false; + data.object.identifier.type = OBJECT_ANALOG_INPUT; + data.object.identifier.instance = 1; + testWhoHasData(pTest, &data); + + for (data.low_limit = 0; + data.low_limit <= BACNET_MAX_INSTANCE; + data.low_limit += (BACNET_MAX_INSTANCE / 4)) { + for (data.high_limit = 0; + data.high_limit <= BACNET_MAX_INSTANCE; + data.high_limit += (BACNET_MAX_INSTANCE / 4)) { + data.object_name = false; + for (data.object.identifier.type = OBJECT_ANALOG_INPUT; + data.object.identifier.type <= MAX_BACNET_OBJECT_TYPE; + data.object.identifier.type++) { + for (data.object.identifier.instance = 1; + data.object.identifier.instance <= BACNET_MAX_INSTANCE; + data.object.identifier.instance <<= 1) { + testWhoHasData(pTest, &data); + } + } + data.object_name = true; + characterstring_init_ansi(&data.object.name, "patricia"); + testWhoHasData(pTest, &data); + } + } +} + +#ifdef TEST_WHOHAS +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Who-Has", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWhoHas); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/whohas.cbp b/bacnet-stack-0-3-0/whohas.cbp new file mode 100644 index 00000000..c9611b46 --- /dev/null +++ b/bacnet-stack-0-3-0/whohas.cbp @@ -0,0 +1,92 @@ + + + + + + + diff --git a/bacnet-stack-0-3-0/whohas.h b/bacnet-stack-0-3-0/whohas.h new file mode 100644 index 00000000..c51e2312 --- /dev/null +++ b/bacnet-stack-0-3-0/whohas.h @@ -0,0 +1,72 @@ +/*####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 WHOHAS_H +#define WHOHAS_H + +#include +#include +#include "bacstr.h" + +typedef struct BACnet_Who_Has_Data { + int32_t low_limit; /* deviceInstanceRange */ + int32_t high_limit; + bool object_name; /* true if a string */ + union { + BACNET_OBJECT_ID identifier; + BACNET_CHARACTER_STRING name; + } object; +} BACNET_WHO_HAS_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service - use -1 for limit if you want unlimited */ + int whohas_encode_apdu(uint8_t * apdu, BACNET_WHO_HAS_DATA * data); + + int whohas_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_WHO_HAS_DATA * data); + + int whohas_decode_apdu(uint8_t * apdu, + unsigned apdu_len, BACNET_WHO_HAS_DATA * data); + +#ifdef TEST +#include "ctest.h" + void testWhoHas(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/whohas.mak b/bacnet-stack-0-3-0/whohas.mak new file mode 100644 index 00000000..180bad1a --- /dev/null +++ b/bacnet-stack-0-3-0/whohas.mak @@ -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_WHOHAS -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + whohas.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = whohas + +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 diff --git a/bacnet-stack-0-3-0/whois.c b/bacnet-stack-0-3-0/whois.c new file mode 100644 index 00000000..4e7b78d7 --- /dev/null +++ b/bacnet-stack-0-3-0/whois.c @@ -0,0 +1,182 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" + +/* encode I-Am service - use -1 for limit if you want unlimited */ +int whois_encode_apdu(uint8_t * apdu, + int32_t low_limit, int32_t high_limit) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_WHO_IS; /* service choice */ + apdu_len = 2; + /* optional limits - must be used as a pair */ + if ((low_limit >= 0) && (low_limit <= BACNET_MAX_INSTANCE) && + (high_limit >= 0) && (high_limit <= BACNET_MAX_INSTANCE)) { + len = encode_context_unsigned(&apdu[apdu_len], 0, low_limit); + apdu_len += len; + len = encode_context_unsigned(&apdu[apdu_len], 1, high_limit); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int whois_decode_service_request(uint8_t * apdu, + unsigned apdu_len, int32_t * pLow_limit, int32_t * pHigh_limit) +{ + int len = 0; + uint8_t tag_number = 0; + uint32_t len_value = 0; + uint32_t decoded_value = 0; + + /* optional limits - must be used as a pair */ + if (apdu_len) { + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number != 0) + return -1; + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) { + if (pLow_limit) + *pLow_limit = decoded_value; + } + len += + decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + if (tag_number != 1) + return -1; + len += decode_unsigned(&apdu[len], len_value, &decoded_value); + if (decoded_value <= BACNET_MAX_INSTANCE) { + if (pHigh_limit) + *pHigh_limit = decoded_value; + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int whois_decode_apdu(uint8_t * apdu, + unsigned apdu_len, int32_t * pLow_limit, int32_t * pHigh_limit) +{ + int len = 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 -1; + if (apdu[1] != SERVICE_UNCONFIRMED_WHO_IS) + return -1; + /* optional limits - must be used as a pair */ + if (apdu_len > 2) { + len = whois_decode_service_request(&apdu[2], + apdu_len - 2, pLow_limit, pHigh_limit); + } + + return len; +} + +void testWhoIs(Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + int32_t low_limit = -1; + int32_t high_limit = -1; + int32_t test_low_limit = -1; + int32_t test_high_limit = -1; + + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + ct_test(pTest, len != 0); + apdu_len = len; + + len = whois_decode_apdu(&apdu[0], + apdu_len, &test_low_limit, &test_high_limit); + ct_test(pTest, len != -1); + ct_test(pTest, test_low_limit == low_limit); + ct_test(pTest, test_high_limit == high_limit); + + for (low_limit = 0; + low_limit <= BACNET_MAX_INSTANCE; + low_limit += (BACNET_MAX_INSTANCE / 4)) { + for (high_limit = 0; + high_limit <= BACNET_MAX_INSTANCE; + high_limit += (BACNET_MAX_INSTANCE / 4)) { + len = whois_encode_apdu(&apdu[0], low_limit, high_limit); + apdu_len = len; + ct_test(pTest, len != 0); + len = whois_decode_apdu(&apdu[0], + apdu_len, &test_low_limit, &test_high_limit); + ct_test(pTest, len != -1); + ct_test(pTest, test_low_limit == low_limit); + ct_test(pTest, test_high_limit == high_limit); + } + } +} + +#ifdef TEST_WHOIS +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Who-Is", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWhoIs); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WHOIS */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/whois.h b/bacnet-stack-0-3-0/whois.h new file mode 100644 index 00000000..aa2cb853 --- /dev/null +++ b/bacnet-stack-0-3-0/whois.h @@ -0,0 +1,61 @@ +/*####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 WHOIS_H +#define WHOIS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service - use -1 for limit if you want unlimited */ + int whois_encode_apdu(uint8_t * apdu, + int32_t low_limit, int32_t high_limit); + + int whois_decode_service_request(uint8_t * apdu, + unsigned apdu_len, int32_t * pLow_limit, int32_t * pHigh_limit); + +#ifdef TEST + int whois_decode_apdu(uint8_t * apdu, + unsigned apdu_len, int32_t * pLow_limit, int32_t * pHigh_limit); + + void testWhoIs(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/whois.mak b/bacnet-stack-0-3-0/whois.mak new file mode 100644 index 00000000..7a962b4c --- /dev/null +++ b/bacnet-stack-0-3-0/whois.mak @@ -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_WHOIS -g + +SRCS = bacdcode.c \ + bacstr.c \ + bigend.c \ + whois.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = whois + +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 diff --git a/bacnet-stack-0-3-0/wp.c b/bacnet-stack-0-3-0/wp.c new file mode 100644 index 00000000..7499f757 --- /dev/null +++ b/bacnet-stack-0-3-0/wp.c @@ -0,0 +1,372 @@ +/*####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 +#include "bacenum.h" +#include "bacdcode.h" +#include "bacdef.h" +#include "device.h" +#include "wp.h" + +/* encode service */ +int wp_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_WRITE_PROPERTY_DATA * data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + int 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_WRITE_PROPERTY; /* service choice */ + apdu_len = 4; + len = encode_context_object_id(&apdu[apdu_len], 0, + data->object_type, data->object_instance); + apdu_len += len; + len = encode_context_enumerated(&apdu[apdu_len], 1, + data->object_property); + apdu_len += len; + /* optional array index; ALL is -1 which is assumed when missing */ + if (data->array_index != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->array_index); + apdu_len += len; + } + /* propertyValue */ + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + for (len = 0; len < data->application_data_len; len++) { + apdu[apdu_len + len] = data->application_data[len]; + } + apdu_len += data->application_data_len; + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + /* optional priority - 0 if not set, 1..16 if set */ + if (data->priority != BACNET_NO_PRIORITY) { + len = encode_context_unsigned(&apdu[apdu_len], 4, + data->priority); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +/* FIXME: there could be various error messages returned + using unique values less than zero */ +int wp_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_WRITE_PROPERTY_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 */ + int property = 0; /* for decoding */ + uint32_t unsigned_value = 0; + int i = 0; /* loop counter */ + + /* check for value pointers */ + if (apdu_len && data) { + /* Tag 0: Object ID */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], &type, &data->object_instance); + data->object_type = type; + /* Tag 1: Property ID */ + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number != 1) + return -1; + len += decode_enumerated(&apdu[len], len_value_type, &property); + data->object_property = property; + /* Tag 2: Optional Array Index */ + /* note: decode without incrementing len so we can check for opening tag */ + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number == 2) { + len += tag_len; + len += decode_unsigned(&apdu[len], len_value_type, + &unsigned_value); + data->array_index = unsigned_value; + } else + data->array_index = BACNET_ARRAY_ALL; + /* Tag 3: opening context tag */ + if (!decode_is_opening_tag_number(&apdu[len], 3)) + return -1; + /* determine the length of the data blob */ + data->application_data_len = bacapp_data_len(&apdu[len], + apdu_len - len, property); + /* a tag number of 3 is not extended so only one octet */ + len++; + /* copy the data from the APDU */ + for (i = 0; i < data->application_data_len; i++) { + data->application_data[i] = apdu[len + i]; + } + /* add on the data length */ + len += data->application_data_len; + if (!decode_is_closing_tag_number(&apdu[len], 3)) + return -2; + /* a tag number of 3 is not extended so only one octet */ + len++; + /* Tag 4: optional Priority - assumed MAX if not explicitly set */ + data->priority = BACNET_MAX_PRIORITY; + if ((unsigned) len < apdu_len) { + tag_len = decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value_type); + if (tag_number == 4) { + len += tag_len; + len = + decode_unsigned(&apdu[len], len_value_type, + &unsigned_value); + if ((unsigned_value >= BACNET_MIN_PRIORITY) + && (unsigned_value <= BACNET_MAX_PRIORITY)) { + data->priority = (uint8_t) unsigned_value; + } else + return -5; + } + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int wp_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, BACNET_WRITE_PROPERTY_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_WRITE_PROPERTY) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = wp_decode_service_request(&apdu[offset], + apdu_len - offset, data); + } + + return len; +} + +void testWritePropertyTag(Test * pTest, + BACNET_APPLICATION_DATA_VALUE * value) +{ + BACNET_WRITE_PROPERTY_DATA data = { 0 }; + BACNET_WRITE_PROPERTY_DATA test_data = { 0 }; + BACNET_APPLICATION_DATA_VALUE test_value; + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 128; + uint8_t test_invoke_id = 0; + + data.application_data_len = + bacapp_encode_application_data(&data.application_data[0], value); + len = wp_encode_apdu(&apdu[0], invoke_id, &data); + ct_test(pTest, len != 0); + /* decode the data */ + apdu_len = len; + len = wp_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.object_property == data.object_property); + ct_test(pTest, test_data.array_index == data.array_index); + /* decode the application value of the request */ + len = bacapp_decode_application_data(test_data.application_data, + test_data.application_data_len, &test_value); + ct_test(pTest, test_value.tag == value->tag); + switch (test_value.tag) { + case BACNET_APPLICATION_TAG_NULL: + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + ct_test(pTest, test_value.type.Boolean == value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + ct_test(pTest, test_value.type.Unsigned_Int == + value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + ct_test(pTest, test_value.type.Signed_Int == + value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + ct_test(pTest, test_value.type.Real == value->type.Real); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + ct_test(pTest, test_value.type.Enumerated == + value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + ct_test(pTest, test_value.type.Date.year == value->type.Date.year); + ct_test(pTest, test_value.type.Date.month == + value->type.Date.month); + ct_test(pTest, test_value.type.Date.day == value->type.Date.day); + ct_test(pTest, test_value.type.Date.wday == value->type.Date.wday); + break; + case BACNET_APPLICATION_TAG_TIME: + ct_test(pTest, test_value.type.Time.hour == value->type.Time.hour); + ct_test(pTest, test_value.type.Time.min == value->type.Time.min); + ct_test(pTest, test_value.type.Time.sec == value->type.Time.sec); + ct_test(pTest, test_value.type.Time.hundredths == + value->type.Time.hundredths); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + ct_test(pTest, test_value.type.Object_Id.type == + value->type.Object_Id.type); + ct_test(pTest, test_value.type.Object_Id.instance == + value->type.Object_Id.instance); + break; + default: + break; + } +} + +void testWriteProperty(Test * pTest) +{ + BACNET_APPLICATION_DATA_VALUE value; + + value.tag = BACNET_APPLICATION_TAG_NULL; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_BOOLEAN; + value.type.Boolean = true; + testWritePropertyTag(pTest, &value); + value.type.Boolean = false; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; + value.type.Unsigned_Int = 0; + testWritePropertyTag(pTest, &value); + value.type.Unsigned_Int = 0xFFFF; + testWritePropertyTag(pTest, &value); + value.type.Unsigned_Int = 0xFFFFFFFF; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_SIGNED_INT; + value.type.Signed_Int = 0; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = -1; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = 32768; + testWritePropertyTag(pTest, &value); + value.type.Signed_Int = -32768; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_REAL; + value.type.Real = 0.0; + testWritePropertyTag(pTest, &value); + value.type.Real = -1.0; + testWritePropertyTag(pTest, &value); + value.type.Real = 1.0; + testWritePropertyTag(pTest, &value); + value.type.Real = 3.14159; + testWritePropertyTag(pTest, &value); + value.type.Real = -3.14159; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_ENUMERATED; + value.type.Enumerated = 0; + testWritePropertyTag(pTest, &value); + value.type.Enumerated = 0xFFFF; + testWritePropertyTag(pTest, &value); + value.type.Enumerated = 0xFFFFFFFF; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_DATE; + value.type.Date.year = 2005; + value.type.Date.month = 5; + value.type.Date.day = 22; + value.type.Date.wday = 1; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_TIME; + value.type.Time.hour = 23; + value.type.Time.min = 59; + value.type.Time.sec = 59; + value.type.Time.hundredths = 12; + testWritePropertyTag(pTest, &value); + + value.tag = BACNET_APPLICATION_TAG_OBJECT_ID; + value.type.Object_Id.type = OBJECT_ANALOG_INPUT; + value.type.Object_Id.instance = 0; + testWritePropertyTag(pTest, &value); + value.type.Object_Id.type = OBJECT_LIFE_SAFETY_ZONE; + value.type.Object_Id.instance = BACNET_MAX_INSTANCE; + testWritePropertyTag(pTest, &value); + + return; +} + +#ifdef TEST_WRITE_PROPERTY +uint16_t Device_Max_APDU_Length_Accepted(void) +{ + return MAX_APDU; +} + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet WriteProperty", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testWriteProperty); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif /* TEST_WRITE_PROPERTY */ +#endif /* TEST */ diff --git a/bacnet-stack-0-3-0/wp.h b/bacnet-stack-0-3-0/wp.h new file mode 100644 index 00000000..57614039 --- /dev/null +++ b/bacnet-stack-0-3-0/wp.h @@ -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 WRITEPROPERTY_H +#define WRITEPROPERTY_H + +#include +#include +#include "bacdcode.h" +#include "bacapp.h" + +/* write property can have application tagged data, or context tagged data, + or even complex data types (i.e. opening and closing tag around data). + It could also have more than one value or element. */ + +typedef struct BACnet_Write_Property_Data { + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + int32_t array_index; /* use BACNET_ARRAY_ALL when not setting */ + uint8_t application_data[MAX_APDU]; + int application_data_len; + uint8_t priority; /* use BACNET_NO_PRIORITY if no priority */ +} BACNET_WRITE_PROPERTY_DATA; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* encode service */ + int wp_encode_apdu(uint8_t * apdu, + uint8_t invoke_id, BACNET_WRITE_PROPERTY_DATA * data); + +/* decode the service request only */ + int wp_decode_service_request(uint8_t * apdu, + unsigned apdu_len, BACNET_WRITE_PROPERTY_DATA * data); + +#ifdef TEST +#include "ctest.h" + int wp_decode_apdu(uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, BACNET_WRITE_PROPERTY_DATA * data); + + void test_ReadProperty(Test * pTest); + void test_ReadPropertyAck(Test * pTest); +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack-0-3-0/wp.mak b/bacnet-stack-0-3-0/wp.mak new file mode 100644 index 00000000..e4025128 --- /dev/null +++ b/bacnet-stack-0-3-0/wp.mak @@ -0,0 +1,37 @@ +#Makefile to build test case +CC = gcc +BASEDIR = . +# -g for debugging with gdb +DEFINES = -DBACFILE=1 -DBACDL_BIP=1 -DTEST -DTEST_WRITE_PROPERTY +INCLUDES = -I. -Idemo/object -Itest +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = bacdcode.c \ + bacstr.c \ + datetime.c \ + bacapp.c \ + bactext.c \ + indtext.c \ + wp.c \ + test/ctest.c + +OBJS = ${SRCS:.c=.o} + +TARGET = writeproperty + +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