From 63e18a0f1fc762344e1dd318e0e1cea749785eb3 Mon Sep 17 00:00:00 2001 From: skarg Date: Fri, 23 Feb 2007 13:19:57 +0000 Subject: [PATCH] Release BACnet Protocol Stack 0.3.0 --- bacnet-stack-0-3-0/.gdbinit | 4 + bacnet-stack-0-3-0/.indent.pro | 1 + bacnet-stack-0-3-0/Makefile | 56 + bacnet-stack-0-3-0/abort.c | 195 ++ bacnet-stack-0-3-0/abort.h | 62 + bacnet-stack-0-3-0/abort.mak | 34 + bacnet-stack-0-3-0/address.c | 366 +++ bacnet-stack-0-3-0/address.h | 74 + bacnet-stack-0-3-0/address.mak | 31 + bacnet-stack-0-3-0/apdu.c | 455 ++++ bacnet-stack-0-3-0/apdu.h | 158 ++ bacnet-stack-0-3-0/arcnet.h | 75 + bacnet-stack-0-3-0/arf.c | 473 ++++ bacnet-stack-0-3-0/arf.h | 102 + bacnet-stack-0-3-0/arf.mak | 34 + bacnet-stack-0-3-0/awf.c | 405 ++++ bacnet-stack-0-3-0/awf.h | 97 + bacnet-stack-0-3-0/awf.mak | 34 + bacnet-stack-0-3-0/bacapp.c | 1195 +++++++++ bacnet-stack-0-3-0/bacapp.h | 136 ++ bacnet-stack-0-3-0/bacapp.ide | Bin 0 -> 33960 bytes bacnet-stack-0-3-0/bacapp.mak | 36 + bacnet-stack-0-3-0/bacdcode.c | 2131 +++++++++++++++++ bacnet-stack-0-3-0/bacdcode.h | 213 ++ bacnet-stack-0-3-0/bacdcode.ide | Bin 0 -> 32336 bytes bacnet-stack-0-3-0/bacdcode.mak | 30 + bacnet-stack-0-3-0/bacdef.h | 99 + bacnet-stack-0-3-0/bacenum.h | 1209 ++++++++++ bacnet-stack-0-3-0/bacerror.c | 259 ++ bacnet-stack-0-3-0/bacerror.h | 74 + bacnet-stack-0-3-0/bacerror.mak | 34 + bacnet-stack-0-3-0/bacprop.c | 111 + bacnet-stack-0-3-0/bacprop.h | 58 + bacnet-stack-0-3-0/bacstr.c | 637 +++++ bacnet-stack-0-3-0/bacstr.h | 141 ++ bacnet-stack-0-3-0/bacstr.mak | 28 + bacnet-stack-0-3-0/bactext.c | 1494 ++++++++++++ bacnet-stack-0-3-0/bactext.h | 81 + bacnet-stack-0-3-0/bigend.c | 28 + bacnet-stack-0-3-0/bigend.h | 29 + bacnet-stack-0-3-0/bip.c | 282 +++ bacnet-stack-0-3-0/bip.h | 99 + bacnet-stack-0-3-0/bits.h | 73 + bacnet-stack-0-3-0/bvlc.c | 624 +++++ bacnet-stack-0-3-0/bvlc.cbp | 98 + bacnet-stack-0-3-0/bvlc.h | 52 + bacnet-stack-0-3-0/bytes.h | 70 + bacnet-stack-0-3-0/comment.sh | 16 + bacnet-stack-0-3-0/config.h | 33 + bacnet-stack-0-3-0/cov.c | 970 ++++++++ bacnet-stack-0-3-0/cov.cbp | 80 + bacnet-stack-0-3-0/cov.h | 120 + bacnet-stack-0-3-0/cov.mak | 37 + bacnet-stack-0-3-0/crc.c | 153 ++ bacnet-stack-0-3-0/crc.h | 51 + bacnet-stack-0-3-0/crc.ide | Bin 0 -> 29482 bytes bacnet-stack-0-3-0/crc.mak | 29 + bacnet-stack-0-3-0/datalink.h | 86 + bacnet-stack-0-3-0/datetime.c | 713 ++++++ bacnet-stack-0-3-0/datetime.h | 112 + bacnet-stack-0-3-0/datetime.ide | Bin 0 -> 30644 bytes bacnet-stack-0-3-0/datetime.mak | 31 + bacnet-stack-0-3-0/dcc.c | 308 +++ bacnet-stack-0-3-0/dcc.h | 86 + bacnet-stack-0-3-0/dcc.mak | 34 + bacnet-stack-0-3-0/demo/dcc/Makefile | 86 + bacnet-stack-0-3-0/demo/dcc/main.c | 282 +++ bacnet-stack-0-3-0/demo/dcc/makefile.b32 | 157 ++ bacnet-stack-0-3-0/demo/dcc/makefile.g++ | 506 ++++ bacnet-stack-0-3-0/demo/dcc/tmake.pro | 59 + bacnet-stack-0-3-0/demo/handler/client.h | 90 + bacnet-stack-0-3-0/demo/handler/h_arf.c | 185 ++ bacnet-stack-0-3-0/demo/handler/h_arf_a.c | 93 + bacnet-stack-0-3-0/demo/handler/h_dcc.c | 128 + bacnet-stack-0-3-0/demo/handler/h_iam.c | 80 + bacnet-stack-0-3-0/demo/handler/h_ihave.c | 60 + bacnet-stack-0-3-0/demo/handler/h_rd.c | 130 + bacnet-stack-0-3-0/demo/handler/h_rp.c | 368 +++ bacnet-stack-0-3-0/demo/handler/h_rp_a.c | 114 + bacnet-stack-0-3-0/demo/handler/h_rp_tiny.c | 122 + bacnet-stack-0-3-0/demo/handler/h_ts.c | 82 + bacnet-stack-0-3-0/demo/handler/h_whohas.c | 85 + bacnet-stack-0-3-0/demo/handler/h_whois.c | 66 + bacnet-stack-0-3-0/demo/handler/h_wp.c | 333 +++ bacnet-stack-0-3-0/demo/handler/handlers.h | 103 + bacnet-stack-0-3-0/demo/handler/noserv.c | 68 + bacnet-stack-0-3-0/demo/handler/s_arfs.c | 112 + bacnet-stack-0-3-0/demo/handler/s_awfs.c | 126 + bacnet-stack-0-3-0/demo/handler/s_dcc.c | 109 + bacnet-stack-0-3-0/demo/handler/s_ihave.c | 83 + bacnet-stack-0-3-0/demo/handler/s_rd.c | 107 + bacnet-stack-0-3-0/demo/handler/s_rp.c | 114 + bacnet-stack-0-3-0/demo/handler/s_ts.c | 100 + bacnet-stack-0-3-0/demo/handler/s_whohas.c | 118 + bacnet-stack-0-3-0/demo/handler/s_whois.c | 75 + bacnet-stack-0-3-0/demo/handler/s_wp.c | 148 ++ bacnet-stack-0-3-0/demo/handler/txbuf.c | 30 + bacnet-stack-0-3-0/demo/handler/txbuf.h | 35 + bacnet-stack-0-3-0/demo/object/ai.c | 197 ++ bacnet-stack-0-3-0/demo/object/ai.h | 55 + bacnet-stack-0-3-0/demo/object/ai.mak | 35 + bacnet-stack-0-3-0/demo/object/ao.c | 469 ++++ bacnet-stack-0-3-0/demo/object/ao.h | 68 + bacnet-stack-0-3-0/demo/object/ao.mak | 38 + bacnet-stack-0-3-0/demo/object/av.c | 418 ++++ bacnet-stack-0-3-0/demo/object/av.h | 60 + bacnet-stack-0-3-0/demo/object/av.mak | 38 + bacnet-stack-0-3-0/demo/object/bacfile.c | 376 +++ bacnet-stack-0-3-0/demo/object/bacfile.h | 79 + bacnet-stack-0-3-0/demo/object/bi.c | 243 ++ bacnet-stack-0-3-0/demo/object/bi.h | 55 + bacnet-stack-0-3-0/demo/object/bi.mak | 35 + bacnet-stack-0-3-0/demo/object/bo.c | 421 ++++ bacnet-stack-0-3-0/demo/object/bo.h | 60 + bacnet-stack-0-3-0/demo/object/bo.mak | 38 + bacnet-stack-0-3-0/demo/object/bv.c | 417 ++++ bacnet-stack-0-3-0/demo/object/bv.h | 60 + bacnet-stack-0-3-0/demo/object/bv.mak | 38 + bacnet-stack-0-3-0/demo/object/device.c | 1114 +++++++++ bacnet-stack-0-3-0/demo/object/device.h | 110 + bacnet-stack-0-3-0/demo/object/device.mak | 40 + bacnet-stack-0-3-0/demo/object/lc.c | 1016 ++++++++ bacnet-stack-0-3-0/demo/object/lc.h | 62 + bacnet-stack-0-3-0/demo/object/lc.ide | Bin 0 -> 35568 bytes bacnet-stack-0-3-0/demo/object/lc.mak | 39 + bacnet-stack-0-3-0/demo/object/lsp.c | 356 +++ bacnet-stack-0-3-0/demo/object/lsp.h | 61 + bacnet-stack-0-3-0/demo/object/lsp.mak | 38 + bacnet-stack-0-3-0/demo/object/mso.c | 433 ++++ bacnet-stack-0-3-0/demo/object/mso.h | 61 + bacnet-stack-0-3-0/demo/object/mso.mak | 38 + bacnet-stack-0-3-0/demo/readfile/Makefile | 83 + bacnet-stack-0-3-0/demo/readfile/main.c | 318 +++ bacnet-stack-0-3-0/demo/readfile/main.ide | Bin 0 -> 53354 bytes bacnet-stack-0-3-0/demo/readfile/makefile.b32 | 155 ++ bacnet-stack-0-3-0/demo/readprop/Makefile | 87 + bacnet-stack-0-3-0/demo/readprop/bacrp.cbp | 369 +++ bacnet-stack-0-3-0/demo/readprop/makefile.b32 | 157 ++ bacnet-stack-0-3-0/demo/readprop/readprop.c | 249 ++ bacnet-stack-0-3-0/demo/reinit/Makefile | 86 + bacnet-stack-0-3-0/demo/reinit/main.c | 268 +++ bacnet-stack-0-3-0/demo/reinit/makefile.b32 | 157 ++ bacnet-stack-0-3-0/demo/server/Makefile | 93 + bacnet-stack-0-3-0/demo/server/epics_vts3.tpi | 759 ++++++ bacnet-stack-0-3-0/demo/server/main.c | 168 ++ bacnet-stack-0-3-0/demo/server/makefile.b32 | 164 ++ bacnet-stack-0-3-0/demo/timesync/Makefile | 87 + bacnet-stack-0-3-0/demo/timesync/main.c | 214 ++ bacnet-stack-0-3-0/demo/timesync/makefile.b32 | 163 ++ bacnet-stack-0-3-0/demo/ucov/Makefile | 82 + bacnet-stack-0-3-0/demo/ucov/main.c | 221 ++ bacnet-stack-0-3-0/demo/ucov/makefile.b32 | 153 ++ bacnet-stack-0-3-0/demo/whohas/Makefile | 87 + bacnet-stack-0-3-0/demo/whohas/main.c | 210 ++ bacnet-stack-0-3-0/demo/whohas/makefile.b32 | 157 ++ bacnet-stack-0-3-0/demo/whois/Makefile | 87 + bacnet-stack-0-3-0/demo/whois/main.c | 227 ++ bacnet-stack-0-3-0/demo/whois/makefile.b32 | 161 ++ bacnet-stack-0-3-0/demo/writefile/Makefile | 84 + .../demo/writefile/makefile.b32 | 156 ++ bacnet-stack-0-3-0/demo/writefile/writefile.c | 286 +++ .../demo/writefile/writefile.ide | Bin 0 -> 60866 bytes bacnet-stack-0-3-0/demo/writeprop/Makefile | 84 + bacnet-stack-0-3-0/demo/writeprop/main.c | 376 +++ .../demo/writeprop/makefile.b32 | 155 ++ bacnet-stack-0-3-0/dlmstp.h | 107 + bacnet-stack-0-3-0/doc/README.build | 9 + bacnet-stack-0-3-0/doc/README.developer | 103 + bacnet-stack-0-3-0/doc/README.msvc | 76 + bacnet-stack-0-3-0/doc/README.release | 67 + bacnet-stack-0-3-0/doc/README.sloc | 31 + bacnet-stack-0-3-0/doc/code-standard.txt | 221 ++ .../doc/htdocs/images/BACnet.png | Bin 0 -> 11912 bytes bacnet-stack-0-3-0/doc/htdocs/index.html | 483 ++++ bacnet-stack-0-3-0/ethernet.h | 76 + bacnet-stack-0-3-0/filename.c | 101 + bacnet-stack-0-3-0/filename.h | 46 + bacnet-stack-0-3-0/filename.ide | Bin 0 -> 29058 bytes bacnet-stack-0-3-0/filename.mak | 32 + bacnet-stack-0-3-0/iam.c | 281 +++ bacnet-stack-0-3-0/iam.h | 67 + bacnet-stack-0-3-0/iam.mak | 37 + bacnet-stack-0-3-0/ihave.c | 196 ++ bacnet-stack-0-3-0/ihave.h | 67 + bacnet-stack-0-3-0/ihave.mak | 34 + bacnet-stack-0-3-0/indent.sh | 27 + bacnet-stack-0-3-0/indtext.c | 228 ++ bacnet-stack-0-3-0/indtext.h | 95 + bacnet-stack-0-3-0/indtext.mak | 28 + bacnet-stack-0-3-0/license/gpl-2.txt | 340 +++ bacnet-stack-0-3-0/license/readme.txt | 90 + bacnet-stack-0-3-0/makefile.b32 | 157 ++ bacnet-stack-0-3-0/mstp.ide | Bin 0 -> 32618 bytes bacnet-stack-0-3-0/mstp.mak | 29 + bacnet-stack-0-3-0/npdu.c | 506 ++++ bacnet-stack-0-3-0/npdu.h | 81 + bacnet-stack-0-3-0/npdu.mak | 36 + bacnet-stack-0-3-0/ports/linux/arcnet.c | 377 +++ bacnet-stack-0-3-0/ports/linux/bip-init.c | 138 ++ bacnet-stack-0-3-0/ports/linux/ethernet.c | 404 ++++ bacnet-stack-0-3-0/ports/linux/main.c | 298 +++ bacnet-stack-0-3-0/ports/linux/net.h | 99 + bacnet-stack-0-3-0/ports/linux/readme.txt | 2 + bacnet-stack-0-3-0/ports/linux/rs485.c | 93 + bacnet-stack-0-3-0/ports/pic18/18f252.lkr | 29 + .../ports/pic18/BACnet-Server.mcp | 131 + .../ports/pic18/BACnet-Server.mcw | Bin 0 -> 23552 bytes bacnet-stack-0-3-0/ports/pic18/bacnet.mcp | 151 ++ bacnet-stack-0-3-0/ports/pic18/bacnet.mcw | Bin 0 -> 28160 bytes bacnet-stack-0-3-0/ports/pic18/device.c | 408 ++++ bacnet-stack-0-3-0/ports/pic18/dlmstp.c | 328 +++ bacnet-stack-0-3-0/ports/pic18/hardware.h | 250 ++ bacnet-stack-0-3-0/ports/pic18/init.c | 130 + bacnet-stack-0-3-0/ports/pic18/init.h | 33 + bacnet-stack-0-3-0/ports/pic18/isr.c | 165 ++ bacnet-stack-0-3-0/ports/pic18/main.c | 192 ++ bacnet-stack-0-3-0/ports/pic18/mstp.c | 1706 +++++++++++++ bacnet-stack-0-3-0/ports/pic18/mstp.h | 246 ++ bacnet-stack-0-3-0/ports/pic18/readme.txt | 30 + bacnet-stack-0-3-0/ports/pic18/rs485.c | 365 +++ bacnet-stack-0-3-0/ports/pic18/rs485.h | 74 + bacnet-stack-0-3-0/ports/pic18/stdbool.h | 28 + bacnet-stack-0-3-0/ports/pic18/stdint.h | 18 + bacnet-stack-0-3-0/ports/pic18/timer.c | 45 + bacnet-stack-0-3-0/ports/pic18/timer.h | 34 + .../ports/pic18f6720/18F6720.lkr | 41 + .../ports/pic18f6720/BACnet-Server.mcp | 147 ++ .../ports/pic18f6720/BACnet-Server.mcw | Bin 0 -> 54272 bytes bacnet-stack-0-3-0/ports/pic18f6720/device.c | 567 +++++ bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.c | 325 +++ bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.h | 110 + .../ports/pic18f6720/hardware.h | 271 +++ bacnet-stack-0-3-0/ports/pic18f6720/isr.c | 190 ++ bacnet-stack-0-3-0/ports/pic18f6720/main.c | 294 +++ bacnet-stack-0-3-0/ports/pic18f6720/mstp.c | 1706 +++++++++++++ bacnet-stack-0-3-0/ports/pic18f6720/mstp.h | 246 ++ bacnet-stack-0-3-0/ports/pic18f6720/rs485.c | 380 +++ bacnet-stack-0-3-0/ports/pic18f6720/rs485.h | 84 + bacnet-stack-0-3-0/ports/pic18f6720/stdbool.h | 28 + bacnet-stack-0-3-0/ports/pic18f6720/stdint.h | 18 + bacnet-stack-0-3-0/ports/rtos32/bip-init.c | 282 +++ bacnet-stack-0-3-0/ports/rtos32/dlmstp.c | 267 +++ bacnet-stack-0-3-0/ports/rtos32/ethernet.c | 337 +++ bacnet-stack-0-3-0/ports/rtos32/hardware.cfg | 24 + bacnet-stack-0-3-0/ports/rtos32/init.c | 124 + bacnet-stack-0-3-0/ports/rtos32/main.c | 172 ++ bacnet-stack-0-3-0/ports/rtos32/makefile.mak | 197 ++ bacnet-stack-0-3-0/ports/rtos32/monitor.cfg | 47 + bacnet-stack-0-3-0/ports/rtos32/mstp.c | 1749 ++++++++++++++ bacnet-stack-0-3-0/ports/rtos32/mstp.h | 244 ++ bacnet-stack-0-3-0/ports/rtos32/net.h | 80 + bacnet-stack-0-3-0/ports/rtos32/netcfg.h | 104 + bacnet-stack-0-3-0/ports/rtos32/rs485.c | 218 ++ bacnet-stack-0-3-0/ports/rtos32/rs485.h | 57 + bacnet-stack-0-3-0/ports/rtos32/setvars.bat | 3 + bacnet-stack-0-3-0/ports/rtos32/software.cfg | 61 + bacnet-stack-0-3-0/ports/rtos32/stdbool.h | 28 + bacnet-stack-0-3-0/ports/rtos32/stdint.h | 19 + bacnet-stack-0-3-0/ports/win32/MAKEFILE.MAK | 151 ++ bacnet-stack-0-3-0/ports/win32/bacnet.ide | Bin 0 -> 69820 bytes .../ports/win32/bacnet/bacnet.dsp | 460 ++++ .../ports/win32/bacnet/bacnet.dsw | 29 + .../ports/win32/bacnet/bacnet.ncb | Bin 0 -> 320512 bytes .../ports/win32/bacnet/bacnet.opt | Bin 0 -> 54784 bytes .../ports/win32/bacnet/bacnet.plg | 136 ++ .../ports/win32/bacnet/readme.txt | 113 + bacnet-stack-0-3-0/ports/win32/bip-init.c | 212 ++ bacnet-stack-0-3-0/ports/win32/ethernet.c | 455 ++++ bacnet-stack-0-3-0/ports/win32/main.c | 262 ++ bacnet-stack-0-3-0/ports/win32/net.h | 38 + bacnet-stack-0-3-0/ports/win32/readme.txt | 15 + bacnet-stack-0-3-0/ports/win32/setvars.bat | 2 + bacnet-stack-0-3-0/ports/win32/stdbool.h | 28 + bacnet-stack-0-3-0/ports/win32/stdint.h | 19 + bacnet-stack-0-3-0/rd.c | 182 ++ bacnet-stack-0-3-0/rd.cbp | 57 + bacnet-stack-0-3-0/rd.h | 70 + bacnet-stack-0-3-0/rd.mak | 34 + bacnet-stack-0-3-0/readme.txt | 106 + bacnet-stack-0-3-0/reject.c | 168 ++ bacnet-stack-0-3-0/reject.h | 60 + bacnet-stack-0-3-0/reject.mak | 34 + bacnet-stack-0-3-0/ringbuf.c | 273 +++ bacnet-stack-0-3-0/ringbuf.h | 71 + bacnet-stack-0-3-0/ringbuf.mak | 29 + bacnet-stack-0-3-0/rp.c | 341 +++ bacnet-stack-0-3-0/rp.h | 84 + bacnet-stack-0-3-0/rp.mak | 34 + bacnet-stack-0-3-0/rpm.c | 744 ++++++ bacnet-stack-0-3-0/rpm.h | 119 + bacnet-stack-0-3-0/rpm.mak | 38 + bacnet-stack-0-3-0/sbuf.c | 209 ++ bacnet-stack-0-3-0/sbuf.h | 80 + bacnet-stack-0-3-0/sbuf.mak | 32 + bacnet-stack-0-3-0/svn2cl.xsl | 215 ++ bacnet-stack-0-3-0/test/ctest.c | 169 ++ bacnet-stack-0-3-0/test/ctest.h | 59 + bacnet-stack-0-3-0/timesync.c | 214 ++ bacnet-stack-0-3-0/timesync.cbp | 126 + bacnet-stack-0-3-0/timesync.h | 66 + bacnet-stack-0-3-0/timesync.mak | 37 + bacnet-stack-0-3-0/tsm.c | 343 +++ bacnet-stack-0-3-0/tsm.h | 124 + bacnet-stack-0-3-0/tsm.mak | 51 + bacnet-stack-0-3-0/unittest.sh | 190 ++ bacnet-stack-0-3-0/whohas.c | 248 ++ bacnet-stack-0-3-0/whohas.cbp | 92 + bacnet-stack-0-3-0/whohas.h | 72 + bacnet-stack-0-3-0/whohas.mak | 34 + bacnet-stack-0-3-0/whois.c | 182 ++ bacnet-stack-0-3-0/whois.h | 61 + bacnet-stack-0-3-0/whois.mak | 34 + bacnet-stack-0-3-0/wp.c | 372 +++ bacnet-stack-0-3-0/wp.h | 81 + bacnet-stack-0-3-0/wp.mak | 37 + 315 files changed, 57198 insertions(+) create mode 100644 bacnet-stack-0-3-0/.gdbinit create mode 100644 bacnet-stack-0-3-0/.indent.pro create mode 100644 bacnet-stack-0-3-0/Makefile create mode 100644 bacnet-stack-0-3-0/abort.c create mode 100644 bacnet-stack-0-3-0/abort.h create mode 100644 bacnet-stack-0-3-0/abort.mak create mode 100644 bacnet-stack-0-3-0/address.c create mode 100644 bacnet-stack-0-3-0/address.h create mode 100644 bacnet-stack-0-3-0/address.mak create mode 100644 bacnet-stack-0-3-0/apdu.c create mode 100644 bacnet-stack-0-3-0/apdu.h create mode 100644 bacnet-stack-0-3-0/arcnet.h create mode 100644 bacnet-stack-0-3-0/arf.c create mode 100644 bacnet-stack-0-3-0/arf.h create mode 100644 bacnet-stack-0-3-0/arf.mak create mode 100644 bacnet-stack-0-3-0/awf.c create mode 100644 bacnet-stack-0-3-0/awf.h create mode 100644 bacnet-stack-0-3-0/awf.mak create mode 100644 bacnet-stack-0-3-0/bacapp.c create mode 100644 bacnet-stack-0-3-0/bacapp.h create mode 100644 bacnet-stack-0-3-0/bacapp.ide create mode 100644 bacnet-stack-0-3-0/bacapp.mak create mode 100644 bacnet-stack-0-3-0/bacdcode.c create mode 100644 bacnet-stack-0-3-0/bacdcode.h create mode 100644 bacnet-stack-0-3-0/bacdcode.ide create mode 100644 bacnet-stack-0-3-0/bacdcode.mak create mode 100644 bacnet-stack-0-3-0/bacdef.h create mode 100644 bacnet-stack-0-3-0/bacenum.h create mode 100644 bacnet-stack-0-3-0/bacerror.c create mode 100644 bacnet-stack-0-3-0/bacerror.h create mode 100644 bacnet-stack-0-3-0/bacerror.mak create mode 100644 bacnet-stack-0-3-0/bacprop.c create mode 100644 bacnet-stack-0-3-0/bacprop.h create mode 100644 bacnet-stack-0-3-0/bacstr.c create mode 100644 bacnet-stack-0-3-0/bacstr.h create mode 100644 bacnet-stack-0-3-0/bacstr.mak create mode 100644 bacnet-stack-0-3-0/bactext.c create mode 100644 bacnet-stack-0-3-0/bactext.h create mode 100644 bacnet-stack-0-3-0/bigend.c create mode 100644 bacnet-stack-0-3-0/bigend.h create mode 100644 bacnet-stack-0-3-0/bip.c create mode 100644 bacnet-stack-0-3-0/bip.h create mode 100644 bacnet-stack-0-3-0/bits.h create mode 100644 bacnet-stack-0-3-0/bvlc.c create mode 100644 bacnet-stack-0-3-0/bvlc.cbp create mode 100644 bacnet-stack-0-3-0/bvlc.h create mode 100644 bacnet-stack-0-3-0/bytes.h create mode 100755 bacnet-stack-0-3-0/comment.sh create mode 100644 bacnet-stack-0-3-0/config.h create mode 100644 bacnet-stack-0-3-0/cov.c create mode 100644 bacnet-stack-0-3-0/cov.cbp create mode 100644 bacnet-stack-0-3-0/cov.h create mode 100644 bacnet-stack-0-3-0/cov.mak create mode 100644 bacnet-stack-0-3-0/crc.c create mode 100644 bacnet-stack-0-3-0/crc.h create mode 100644 bacnet-stack-0-3-0/crc.ide create mode 100644 bacnet-stack-0-3-0/crc.mak create mode 100644 bacnet-stack-0-3-0/datalink.h create mode 100644 bacnet-stack-0-3-0/datetime.c create mode 100644 bacnet-stack-0-3-0/datetime.h create mode 100644 bacnet-stack-0-3-0/datetime.ide create mode 100644 bacnet-stack-0-3-0/datetime.mak create mode 100644 bacnet-stack-0-3-0/dcc.c create mode 100644 bacnet-stack-0-3-0/dcc.h create mode 100644 bacnet-stack-0-3-0/dcc.mak create mode 100644 bacnet-stack-0-3-0/demo/dcc/Makefile create mode 100644 bacnet-stack-0-3-0/demo/dcc/main.c create mode 100644 bacnet-stack-0-3-0/demo/dcc/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/dcc/makefile.g++ create mode 100644 bacnet-stack-0-3-0/demo/dcc/tmake.pro create mode 100644 bacnet-stack-0-3-0/demo/handler/client.h create mode 100644 bacnet-stack-0-3-0/demo/handler/h_arf.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_arf_a.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_dcc.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_iam.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_ihave.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_rd.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_rp.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_rp_a.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_rp_tiny.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_ts.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_whohas.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_whois.c create mode 100644 bacnet-stack-0-3-0/demo/handler/h_wp.c create mode 100644 bacnet-stack-0-3-0/demo/handler/handlers.h create mode 100644 bacnet-stack-0-3-0/demo/handler/noserv.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_arfs.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_awfs.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_dcc.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_ihave.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_rd.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_rp.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_ts.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_whohas.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_whois.c create mode 100644 bacnet-stack-0-3-0/demo/handler/s_wp.c create mode 100644 bacnet-stack-0-3-0/demo/handler/txbuf.c create mode 100644 bacnet-stack-0-3-0/demo/handler/txbuf.h create mode 100644 bacnet-stack-0-3-0/demo/object/ai.c create mode 100644 bacnet-stack-0-3-0/demo/object/ai.h create mode 100755 bacnet-stack-0-3-0/demo/object/ai.mak create mode 100644 bacnet-stack-0-3-0/demo/object/ao.c create mode 100644 bacnet-stack-0-3-0/demo/object/ao.h create mode 100755 bacnet-stack-0-3-0/demo/object/ao.mak create mode 100644 bacnet-stack-0-3-0/demo/object/av.c create mode 100644 bacnet-stack-0-3-0/demo/object/av.h create mode 100644 bacnet-stack-0-3-0/demo/object/av.mak create mode 100644 bacnet-stack-0-3-0/demo/object/bacfile.c create mode 100644 bacnet-stack-0-3-0/demo/object/bacfile.h create mode 100644 bacnet-stack-0-3-0/demo/object/bi.c create mode 100644 bacnet-stack-0-3-0/demo/object/bi.h create mode 100644 bacnet-stack-0-3-0/demo/object/bi.mak create mode 100644 bacnet-stack-0-3-0/demo/object/bo.c create mode 100644 bacnet-stack-0-3-0/demo/object/bo.h create mode 100755 bacnet-stack-0-3-0/demo/object/bo.mak create mode 100644 bacnet-stack-0-3-0/demo/object/bv.c create mode 100644 bacnet-stack-0-3-0/demo/object/bv.h create mode 100644 bacnet-stack-0-3-0/demo/object/bv.mak create mode 100644 bacnet-stack-0-3-0/demo/object/device.c create mode 100644 bacnet-stack-0-3-0/demo/object/device.h create mode 100644 bacnet-stack-0-3-0/demo/object/device.mak create mode 100644 bacnet-stack-0-3-0/demo/object/lc.c create mode 100644 bacnet-stack-0-3-0/demo/object/lc.h create mode 100644 bacnet-stack-0-3-0/demo/object/lc.ide create mode 100644 bacnet-stack-0-3-0/demo/object/lc.mak create mode 100644 bacnet-stack-0-3-0/demo/object/lsp.c create mode 100644 bacnet-stack-0-3-0/demo/object/lsp.h create mode 100755 bacnet-stack-0-3-0/demo/object/lsp.mak create mode 100644 bacnet-stack-0-3-0/demo/object/mso.c create mode 100644 bacnet-stack-0-3-0/demo/object/mso.h create mode 100644 bacnet-stack-0-3-0/demo/object/mso.mak create mode 100644 bacnet-stack-0-3-0/demo/readfile/Makefile create mode 100644 bacnet-stack-0-3-0/demo/readfile/main.c create mode 100644 bacnet-stack-0-3-0/demo/readfile/main.ide create mode 100644 bacnet-stack-0-3-0/demo/readfile/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/readprop/Makefile create mode 100644 bacnet-stack-0-3-0/demo/readprop/bacrp.cbp create mode 100644 bacnet-stack-0-3-0/demo/readprop/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/readprop/readprop.c create mode 100644 bacnet-stack-0-3-0/demo/reinit/Makefile create mode 100644 bacnet-stack-0-3-0/demo/reinit/main.c create mode 100644 bacnet-stack-0-3-0/demo/reinit/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/server/Makefile create mode 100644 bacnet-stack-0-3-0/demo/server/epics_vts3.tpi create mode 100644 bacnet-stack-0-3-0/demo/server/main.c create mode 100644 bacnet-stack-0-3-0/demo/server/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/timesync/Makefile create mode 100644 bacnet-stack-0-3-0/demo/timesync/main.c create mode 100644 bacnet-stack-0-3-0/demo/timesync/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/ucov/Makefile create mode 100644 bacnet-stack-0-3-0/demo/ucov/main.c create mode 100644 bacnet-stack-0-3-0/demo/ucov/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/whohas/Makefile create mode 100644 bacnet-stack-0-3-0/demo/whohas/main.c create mode 100644 bacnet-stack-0-3-0/demo/whohas/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/whois/Makefile create mode 100644 bacnet-stack-0-3-0/demo/whois/main.c create mode 100644 bacnet-stack-0-3-0/demo/whois/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/writefile/Makefile create mode 100644 bacnet-stack-0-3-0/demo/writefile/makefile.b32 create mode 100644 bacnet-stack-0-3-0/demo/writefile/writefile.c create mode 100644 bacnet-stack-0-3-0/demo/writefile/writefile.ide create mode 100644 bacnet-stack-0-3-0/demo/writeprop/Makefile create mode 100644 bacnet-stack-0-3-0/demo/writeprop/main.c create mode 100644 bacnet-stack-0-3-0/demo/writeprop/makefile.b32 create mode 100644 bacnet-stack-0-3-0/dlmstp.h create mode 100644 bacnet-stack-0-3-0/doc/README.build create mode 100644 bacnet-stack-0-3-0/doc/README.developer create mode 100644 bacnet-stack-0-3-0/doc/README.msvc create mode 100644 bacnet-stack-0-3-0/doc/README.release create mode 100644 bacnet-stack-0-3-0/doc/README.sloc create mode 100644 bacnet-stack-0-3-0/doc/code-standard.txt create mode 100644 bacnet-stack-0-3-0/doc/htdocs/images/BACnet.png create mode 100644 bacnet-stack-0-3-0/doc/htdocs/index.html create mode 100644 bacnet-stack-0-3-0/ethernet.h create mode 100644 bacnet-stack-0-3-0/filename.c create mode 100644 bacnet-stack-0-3-0/filename.h create mode 100644 bacnet-stack-0-3-0/filename.ide create mode 100644 bacnet-stack-0-3-0/filename.mak create mode 100755 bacnet-stack-0-3-0/iam.c create mode 100644 bacnet-stack-0-3-0/iam.h create mode 100755 bacnet-stack-0-3-0/iam.mak create mode 100644 bacnet-stack-0-3-0/ihave.c create mode 100644 bacnet-stack-0-3-0/ihave.h create mode 100644 bacnet-stack-0-3-0/ihave.mak create mode 100755 bacnet-stack-0-3-0/indent.sh create mode 100644 bacnet-stack-0-3-0/indtext.c create mode 100644 bacnet-stack-0-3-0/indtext.h create mode 100644 bacnet-stack-0-3-0/indtext.mak create mode 100644 bacnet-stack-0-3-0/license/gpl-2.txt create mode 100644 bacnet-stack-0-3-0/license/readme.txt create mode 100644 bacnet-stack-0-3-0/makefile.b32 create mode 100644 bacnet-stack-0-3-0/mstp.ide create mode 100644 bacnet-stack-0-3-0/mstp.mak create mode 100644 bacnet-stack-0-3-0/npdu.c create mode 100644 bacnet-stack-0-3-0/npdu.h create mode 100644 bacnet-stack-0-3-0/npdu.mak create mode 100644 bacnet-stack-0-3-0/ports/linux/arcnet.c create mode 100644 bacnet-stack-0-3-0/ports/linux/bip-init.c create mode 100644 bacnet-stack-0-3-0/ports/linux/ethernet.c create mode 100644 bacnet-stack-0-3-0/ports/linux/main.c create mode 100644 bacnet-stack-0-3-0/ports/linux/net.h create mode 100644 bacnet-stack-0-3-0/ports/linux/readme.txt create mode 100644 bacnet-stack-0-3-0/ports/linux/rs485.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/18f252.lkr create mode 100644 bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcp create mode 100644 bacnet-stack-0-3-0/ports/pic18/BACnet-Server.mcw create mode 100644 bacnet-stack-0-3-0/ports/pic18/bacnet.mcp create mode 100644 bacnet-stack-0-3-0/ports/pic18/bacnet.mcw create mode 100644 bacnet-stack-0-3-0/ports/pic18/device.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/dlmstp.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/hardware.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/init.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/init.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/isr.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/main.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/mstp.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/mstp.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/readme.txt create mode 100644 bacnet-stack-0-3-0/ports/pic18/rs485.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/rs485.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/stdbool.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/stdint.h create mode 100644 bacnet-stack-0-3-0/ports/pic18/timer.c create mode 100644 bacnet-stack-0-3-0/ports/pic18/timer.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/18F6720.lkr create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcp create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/BACnet-Server.mcw create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/device.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/dlmstp.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/hardware.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/isr.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/main.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/mstp.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/mstp.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/rs485.c create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/rs485.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/stdbool.h create mode 100644 bacnet-stack-0-3-0/ports/pic18f6720/stdint.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/bip-init.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/dlmstp.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/ethernet.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/hardware.cfg create mode 100644 bacnet-stack-0-3-0/ports/rtos32/init.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/main.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/makefile.mak create mode 100644 bacnet-stack-0-3-0/ports/rtos32/monitor.cfg create mode 100644 bacnet-stack-0-3-0/ports/rtos32/mstp.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/mstp.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/net.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/netcfg.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/rs485.c create mode 100644 bacnet-stack-0-3-0/ports/rtos32/rs485.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/setvars.bat create mode 100644 bacnet-stack-0-3-0/ports/rtos32/software.cfg create mode 100644 bacnet-stack-0-3-0/ports/rtos32/stdbool.h create mode 100644 bacnet-stack-0-3-0/ports/rtos32/stdint.h create mode 100644 bacnet-stack-0-3-0/ports/win32/MAKEFILE.MAK create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet.ide create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsp create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.dsw create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.ncb create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.opt create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/bacnet.plg create mode 100644 bacnet-stack-0-3-0/ports/win32/bacnet/readme.txt create mode 100644 bacnet-stack-0-3-0/ports/win32/bip-init.c create mode 100644 bacnet-stack-0-3-0/ports/win32/ethernet.c create mode 100644 bacnet-stack-0-3-0/ports/win32/main.c create mode 100644 bacnet-stack-0-3-0/ports/win32/net.h create mode 100644 bacnet-stack-0-3-0/ports/win32/readme.txt create mode 100644 bacnet-stack-0-3-0/ports/win32/setvars.bat create mode 100644 bacnet-stack-0-3-0/ports/win32/stdbool.h create mode 100644 bacnet-stack-0-3-0/ports/win32/stdint.h create mode 100644 bacnet-stack-0-3-0/rd.c create mode 100644 bacnet-stack-0-3-0/rd.cbp create mode 100644 bacnet-stack-0-3-0/rd.h create mode 100644 bacnet-stack-0-3-0/rd.mak create mode 100644 bacnet-stack-0-3-0/readme.txt create mode 100644 bacnet-stack-0-3-0/reject.c create mode 100644 bacnet-stack-0-3-0/reject.h create mode 100644 bacnet-stack-0-3-0/reject.mak create mode 100644 bacnet-stack-0-3-0/ringbuf.c create mode 100644 bacnet-stack-0-3-0/ringbuf.h create mode 100644 bacnet-stack-0-3-0/ringbuf.mak create mode 100644 bacnet-stack-0-3-0/rp.c create mode 100644 bacnet-stack-0-3-0/rp.h create mode 100644 bacnet-stack-0-3-0/rp.mak create mode 100644 bacnet-stack-0-3-0/rpm.c create mode 100644 bacnet-stack-0-3-0/rpm.h create mode 100644 bacnet-stack-0-3-0/rpm.mak create mode 100644 bacnet-stack-0-3-0/sbuf.c create mode 100644 bacnet-stack-0-3-0/sbuf.h create mode 100644 bacnet-stack-0-3-0/sbuf.mak create mode 100644 bacnet-stack-0-3-0/svn2cl.xsl create mode 100644 bacnet-stack-0-3-0/test/ctest.c create mode 100644 bacnet-stack-0-3-0/test/ctest.h create mode 100644 bacnet-stack-0-3-0/timesync.c create mode 100644 bacnet-stack-0-3-0/timesync.cbp create mode 100644 bacnet-stack-0-3-0/timesync.h create mode 100644 bacnet-stack-0-3-0/timesync.mak create mode 100644 bacnet-stack-0-3-0/tsm.c create mode 100644 bacnet-stack-0-3-0/tsm.h create mode 100644 bacnet-stack-0-3-0/tsm.mak create mode 100755 bacnet-stack-0-3-0/unittest.sh create mode 100644 bacnet-stack-0-3-0/whohas.c create mode 100644 bacnet-stack-0-3-0/whohas.cbp create mode 100644 bacnet-stack-0-3-0/whohas.h create mode 100644 bacnet-stack-0-3-0/whohas.mak create mode 100644 bacnet-stack-0-3-0/whois.c create mode 100644 bacnet-stack-0-3-0/whois.h create mode 100644 bacnet-stack-0-3-0/whois.mak create mode 100644 bacnet-stack-0-3-0/wp.c create mode 100644 bacnet-stack-0-3-0/wp.h create mode 100644 bacnet-stack-0-3-0/wp.mak 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 0000000000000000000000000000000000000000..9beee0ee26afea5bdde95efdd5011e3684db9312 GIT binary patch literal 33960 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 T4p-k~={g*~$6>IzkTCxji6d>` literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9dabaa4b2372a62a7f73337d2e5e18ecef7635ee GIT binary patch literal 32336 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 .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 0000000000000000000000000000000000000000..d7b2030c0a8affcc080f4ca37a959c5ec8bf8fc2 GIT binary patch literal 29482 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..89b2b3f7e553b08cb1e4b238084a034f0f6f17d3 GIT binary patch literal 30644 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..682eafcb4bf32957444cbba34e713c2dfa6acbb5 GIT binary patch literal 35568 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..ba4fbdc8476bfb6d6966b21e657d364edf4ad051 GIT binary patch literal 53354 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 J#0nwj{{Zyf=H~za literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..df787ac9e48e88b6c7fd059a33a26ace16885d56 GIT binary patch literal 60866 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..29c7b57d76b00b0018ae0d01fa056a4f69de840c GIT binary patch literal 11912 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 KiskZ_U;iH+ + + + + 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 0000000000000000000000000000000000000000..38d3cdf89835c1f3900a7dc7b61f4d02ea1178de GIT binary patch literal 29058 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..986ae620e955c969ec744a59789437d9b6f90257 GIT binary patch literal 32618 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 Mz1d-~x{xye2e+6(n*aa+ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..eb35cf22e215c2a4fcb135b4d507a90ece8d28ac GIT binary patch literal 23552 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..825a749ee61b35029237a7ab652ea8c63d160527 GIT binary patch literal 54272 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 K7Rmc@#{YkcUPT}P literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..566cabcf33890422889301df2bc9fdd9d3be7533 GIT binary patch literal 69820 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 +# 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 0000000000000000000000000000000000000000..4914a281c9d8f810ea25d85861a595323b9090ac GIT binary patch literal 320512 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%0Dnv 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 literal 0 HcmV?d00001 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