diff --git a/bacnet-stack/demo/handler/h_getevent.c b/bacnet-stack/demo/handler/h_getevent.c new file mode 100644 index 00000000..846e3860 --- /dev/null +++ b/bacnet-stack/demo/handler/h_getevent.c @@ -0,0 +1,176 @@ +/************************************************************************** +* +* Copyright (C) 2009 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 "event.h" + +static uint8_t Temp_Buf[MAX_APDU] = { 0 }; + +static get_event_info_function + Get_Event_Info[MAX_BACNET_OBJECT_TYPE]; + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, + get_event_info_function pFunction) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) { + Get_Event_Info[object_type] = pFunction; + } +} + +void handler_get_event_information( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_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; + BACNET_OBJECT_ID object_id; + unsigned i = 0; /* counter */ + BACNET_GET_EVENT_INFORMATION_DATA getevent_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 (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, "GetEventInformation: " + "Segmented message. Sending Abort!\n"); +#endif + goto GET_EVENT_ABORT; + } + + len = getevent_decode_service_request( + service_request, + service_len, + &object_id); + 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, "GetEventInformation: Bad Encoding. Sending Abort!\n"); +#endif + goto GET_EVENT_ABORT; + } + + /* assume that there is an error */ + error = true; + len = getevent_ack_encode_apdu_init( + &Handler_Transmit_Buffer[pdu_len], + sizeof(Handler_Transmit_Buffer) - pdu_len, + service_data->invoke_id); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } + pdu_len += len; + for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) { + if (Get_Event_Info[i]) { + Get_Event_Info[i](&getevent_data); + len = getevent_ack_encode_apdu_data( + &Handler_Transmit_Buffer[pdu_len], + sizeof(Handler_Transmit_Buffer)-pdu_len, + &getevent_data); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } + pdu_len += len; + } + } + len = getevent_ack_encode_apdu_end( + &Handler_Transmit_Buffer[pdu_len], + sizeof(Handler_Transmit_Buffer)-pdu_len, + false); + if (len <= 0) { + error = true; + goto GET_EVENT_ERROR; + } + pdu_len += len; +#if PRINT_ENABLED + fprintf(stderr, "GetEventInformation: Sending Ack!\n"); +#endif +GET_EVENT_ERROR: + if (error) { + if (len == -2) { + /* BACnet APDU too small to fit data, so proper response is 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, "GetEventInformation: " + "Reply too big to fit into APDU!\n"); +#endif + } else { + len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY, + error_class, error_code); +#if PRINT_ENABLED + fprintf(stderr, "GetEventInformation: Sending Error!\n"); +#endif + } + } +GET_EVENT_ABORT: + 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/include/getevent.h b/bacnet-stack/include/getevent.h new file mode 100644 index 00000000..f3a4677d --- /dev/null +++ b/bacnet-stack/include/getevent.h @@ -0,0 +1,120 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 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 GETEVENT_H +#define GETEVENT_H + +#include +#include +#include "bacdef.h" +#include "bacenum.h" +#include "timestamp.h" +#include "event.h" + +struct BACnet_Get_Event_Information_Data; +typedef struct BACnet_Get_Event_Information_Data { + BACNET_OBJECT_ID objectIdentifier; + BACNET_EVENT_STATE eventState; + BACNET_BIT_STRING acknowledgedTransitions; + BACNET_TIMESTAMP eventTimeStamps[3]; + BACNET_NOTIFY_TYPE notifyType; + BACNET_BIT_STRING eventEnable; + uint32_t eventPriorities[3]; + struct BACnet_Get_Event_Information_Data *next; +} BACNET_GET_EVENT_INFORMATION_DATA; + +typedef int (*get_event_info_function) ( + BACNET_GET_EVENT_INFORMATION_DATA *getevent_data); + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int getevent_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier); + +int getevent_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_ID * object_id); + +int getevent_ack_encode_apdu_init( + uint8_t * apdu, + size_t max_apdu, + uint8_t invoke_id); + +int getevent_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data); + +int getevent_ack_encode_apdu_end( + uint8_t * apdu, + size_t max_apdu, + bool moreEvents); + +int getevent_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool *moreEvents); + +#ifdef TEST +#include "ctest.h" +int getevent_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier); + +int getevent_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool *moreEvents); + +void testGetEventInformationAck( + Test * pTest); + +void testGetEventInformation( + Test * pTest); + +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index c2d1a8cb..0b15faa5 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -34,6 +34,7 @@ #include "rp.h" #include "rpm.h" #include "wp.h" +#include "getevent.h" #ifdef __cplusplus @@ -224,6 +225,17 @@ extern "C" { uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data); + + void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, + get_event_info_function pFunction); + + void handler_get_event_information( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + #ifdef __cplusplus } diff --git a/bacnet-stack/include/timestamp.h b/bacnet-stack/include/timestamp.h index 154c409a..5bbb5251 100644 --- a/bacnet-stack/include/timestamp.h +++ b/bacnet-stack/include/timestamp.h @@ -66,12 +66,15 @@ extern "C" { int bacapp_encode_timestamp( uint8_t * apdu, BACNET_TIMESTAMP * value); + int bacapp_decode_timestamp( + uint8_t * apdu, + BACNET_TIMESTAMP * value); + int bacapp_encode_context_timestamp( uint8_t * apdu, uint8_t tag_number, BACNET_TIMESTAMP * value); - int bacapp_decode_context_timestamp( uint8_t * apdu, uint8_t tag_number, diff --git a/bacnet-stack/src/bacdcode.c b/bacnet-stack/src/bacdcode.c index dd5dc57b..79fc2cfd 100644 --- a/bacnet-stack/src/bacdcode.c +++ b/bacnet-stack/src/bacdcode.c @@ -325,26 +325,26 @@ int decode_tag_number( /* Same as function above, but will safely fail is packet has been truncated */ int decode_tag_number_safe( uint8_t * apdu, - uint32_t apdu_len_remaining, + uint32_t apdu_len_remaining, uint8_t * tag_number) { int len = 0; /* return value */ /* decode the tag number first */ - if ( apdu_len_remaining >= 1 ) { - if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_remaining >= 2) { - /* extended tag */ - if (tag_number) { - *tag_number = apdu[1]; - } - len = 2; - } else { - if (tag_number) { - *tag_number = (uint8_t) (apdu[0] >> 4); - } - len = 1; - } - } + if ( apdu_len_remaining >= 1 ) { + if (IS_EXTENDED_TAG_NUMBER(apdu[0]) && apdu_len_remaining >= 2) { + /* extended tag */ + if (tag_number) { + *tag_number = apdu[1]; + } + len = 2; + } else { + if (tag_number) { + *tag_number = (uint8_t) (apdu[0] >> 4); + } + len = 1; + } + } return len; } @@ -415,56 +415,56 @@ int decode_tag_number_and_value( /* Same as function above, but will safely fail is packet has been truncated */ int decode_tag_number_and_value_safe( uint8_t * apdu, - uint32_t apdu_len_remaining, + uint32_t apdu_len_remaining, uint8_t * tag_number, uint32_t * value) { int len = 0; - len = decode_tag_number_safe(&apdu[0], apdu_len_remaining, tag_number); + len = decode_tag_number_safe(&apdu[0], apdu_len_remaining, tag_number); - if ( len > 0 ) { - apdu_len_remaining -= len; - if (IS_EXTENDED_VALUE(apdu[0])) { - /* tagged as uint32_t */ - if (apdu[len] == 255 && apdu_len_remaining >= 5) { - uint32_t value32; - len++; - len += decode_unsigned32(&apdu[len], &value32); - if (value) { - *value = value32; - } - } - /* tagged as uint16_t */ - else if (apdu[len] == 254 && apdu_len_remaining >= 3) { - uint16_t value16; - len++; - len += decode_unsigned16(&apdu[len], &value16); - if (value) { - *value = value16; - } - } - /* no tag - must be uint8_t */ - else if (apdu[len] < 254 && apdu_len_remaining >= 1) { - if (value) { - *value = apdu[len]; - } - len++; - } - else { - /* packet is truncated */ - len = 0; - } - } else if (decode_is_opening_tag(&apdu[0]) && value) { - *value = 0; - } else if (decode_is_closing_tag(&apdu[0]) && value) { - /* closing tag */ - *value = 0; - } else if (value) { - /* small value */ - *value = apdu[0] & 0x07; - } - } + if ( len > 0 ) { + apdu_len_remaining -= len; + if (IS_EXTENDED_VALUE(apdu[0])) { + /* tagged as uint32_t */ + if (apdu[len] == 255 && apdu_len_remaining >= 5) { + uint32_t value32; + len++; + len += decode_unsigned32(&apdu[len], &value32); + if (value) { + *value = value32; + } + } + /* tagged as uint16_t */ + else if (apdu[len] == 254 && apdu_len_remaining >= 3) { + uint16_t value16; + len++; + len += decode_unsigned16(&apdu[len], &value16); + if (value) { + *value = value16; + } + } + /* no tag - must be uint8_t */ + else if (apdu[len] < 254 && apdu_len_remaining >= 1) { + if (value) { + *value = apdu[len]; + } + len++; + } + else { + /* packet is truncated */ + len = 0; + } + } else if (decode_is_opening_tag(&apdu[0]) && value) { + *value = 0; + } else if (decode_is_closing_tag(&apdu[0]) && value) { + /* closing tag */ + *value = 0; + } else if (value) { + /* small value */ + *value = apdu[0] & 0x07; + } + } return len; } @@ -784,16 +784,16 @@ int decode_object_id( int decode_object_id_safe( uint8_t * apdu, - uint32_t len_value, + uint32_t len_value, uint16_t * object_type, uint32_t * instance) { - if ( len_value != 4 ) { - return 0; - } - else { - return decode_object_id(apdu, object_type, instance); - } + if ( len_value != 4 ) { + return 0; + } + else { + return decode_object_id(apdu, object_type, instance); + } } int decode_context_object_id( @@ -1181,7 +1181,7 @@ int encode_context_unsigned( uint32_t value) { int len = 0; - + /* length of unsigned is variable, as per 20.2.4 */ if (value < 0x100) { len = 1; @@ -1193,8 +1193,8 @@ int encode_context_unsigned( len = 4; } - len = encode_tag(&apdu[0], tag_number, true, len); - len += encode_bacnet_unsigned(&apdu[len], value); + len = encode_tag(&apdu[0], tag_number, true, len); + len += encode_bacnet_unsigned(&apdu[len], value); return len; } @@ -1306,7 +1306,7 @@ int encode_context_enumerated( len = encode_tag(&apdu[0], tag_number, true, (uint32_t) len); len += encode_bacnet_enumerated(&apdu[len], value); - return len; + return len; } /* from clause 20.2.5 Encoding of a Signed Integer Value */ @@ -1558,21 +1558,21 @@ int decode_bacnet_time( int decode_bacnet_time_safe( uint8_t * apdu, - uint32_t len_value, + uint32_t len_value, BACNET_TIME * btime) { - if ( len_value != 4 ) - { - btime->hour = 0; - btime->hundredths = 0; - btime->min = 0; - btime->sec = 0; - return len_value; - } - else - { - return decode_bacnet_time(apdu, btime); - } + if ( len_value != 4 ) + { + btime->hour = 0; + btime->hundredths = 0; + btime->min = 0; + btime->sec = 0; + return len_value; + } + else + { + return decode_bacnet_time(apdu, btime); + } } int decode_application_time( @@ -1694,19 +1694,19 @@ int decode_date( int decode_date_safe( uint8_t * apdu, - uint32_t len_value, + uint32_t len_value, BACNET_DATE * bdate) { - if ( len_value != 4 ) { - bdate->day = 0; - bdate->month = 0; - bdate->wday = 0; - bdate->year = 0; - return len_value; - } - else { - return decode_date(apdu, bdate); - } + if ( len_value != 4 ) { + bdate->day = 0; + bdate->month = 0; + bdate->wday = 0; + bdate->year = 0; + return len_value; + } + else { + return decode_date(apdu, bdate); + } } @@ -2380,7 +2380,7 @@ void testUnsignedContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 32 bit number */ uint32_t in = 0xdeadbeef; @@ -2404,7 +2404,7 @@ void testUnsignedContextDecodes( ct_test(pTest, in == out); ct_test(pTest, outLen2 == -1); - /* 16 bit number */ + /* 16 bit number */ in = 0xdead; inLen = encode_context_unsigned(apdu, 10, in); outLen = decode_context_unsigned(apdu, 10, &out); @@ -2457,7 +2457,7 @@ void testUnsignedContextDecodes( ct_test(pTest, inLen == outLen); ct_test(pTest, in == out); - inLen = encode_context_unsigned(apdu, large_context_tag, in); + inLen = encode_context_unsigned(apdu, large_context_tag, in); outLen = decode_context_unsigned(apdu, large_context_tag, &out); outLen2 = decode_context_unsigned(apdu, large_context_tag-1, &out); @@ -2474,7 +2474,7 @@ void testSignedContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 32 bit number */ @@ -2555,7 +2555,7 @@ void testSignedContextDecodes( ct_test(pTest, inLen == outLen); ct_test(pTest, in == out); - inLen = encode_context_signed(apdu, large_context_tag, in); + inLen = encode_context_signed(apdu, large_context_tag, in); outLen = decode_context_signed(apdu, large_context_tag, &out); outLen2 = decode_context_signed(apdu, large_context_tag-1, &out); @@ -2572,7 +2572,7 @@ void testEnumeratedContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 32 bit number */ uint32_t in = 0xdeadbeef; @@ -2649,7 +2649,7 @@ void testEnumeratedContextDecodes( ct_test(pTest, inLen == outLen); ct_test(pTest, in == out); - inLen = encode_context_enumerated(apdu, large_context_tag, in); + inLen = encode_context_enumerated(apdu, large_context_tag, in); outLen = decode_context_enumerated(apdu, large_context_tag, &out); outLen2 = decode_context_enumerated(apdu, large_context_tag-1, &out); @@ -2665,7 +2665,7 @@ void testFloatContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 32 bit number */ float in; @@ -2713,7 +2713,7 @@ void testDoubleContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 64 bit number */ double in; @@ -2761,7 +2761,7 @@ static void testObjectIDContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; /* 32 bit number */ uint16_t in_type; @@ -2799,7 +2799,7 @@ static void testCharacterStringContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; BACNET_CHARACTER_STRING in; @@ -2835,7 +2835,7 @@ void testBitStringContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; BACNET_BIT_STRING in; BACNET_BIT_STRING out; @@ -2874,7 +2874,7 @@ void testOctetStringContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; BACNET_OCTET_STRING in; @@ -2911,7 +2911,7 @@ void testTimeContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; BACNET_TIME in; BACNET_TIME out; @@ -2953,7 +2953,7 @@ void testDateContextDecodes( int inLen; int outLen; int outLen2; - uint8_t large_context_tag = 0xfe; + uint8_t large_context_tag = 0xfe; BACNET_DATE in; @@ -2968,19 +2968,19 @@ void testDateContextDecodes( outLen = decode_context_date(apdu, 10, &out); outLen2 = decode_context_date(apdu, 9, &out); - ct_test(pTest, outLen2 == -1); + ct_test(pTest, outLen2 == -1); ct_test(pTest, inLen == outLen); ct_test(pTest, in.day == out.day); ct_test(pTest, in.month == out.month); ct_test(pTest, in.wday == out.wday); ct_test(pTest, in.year == out.year); - /* Test large tags */ - inLen = encode_context_date(apdu, large_context_tag, &in); + /* Test large tags */ + inLen = encode_context_date(apdu, large_context_tag, &in); outLen = decode_context_date(apdu, large_context_tag, &out); outLen2 = decode_context_date(apdu, large_context_tag-1, &out); - ct_test(pTest, inLen == outLen); + ct_test(pTest, inLen == outLen); ct_test(pTest, in.day == out.day); ct_test(pTest, in.month == out.month); ct_test(pTest, in.wday == out.wday); @@ -2995,7 +2995,7 @@ int main( Test *pTest; bool rc; - pTest = ct_create("BACDCode", NULL); + pTest = ct_create("BACDCode", NULL); /* individual tests */ rc = ct_addTestFunction(pTest, testBACDCodeTags); assert(rc); diff --git a/bacnet-stack/src/getevent.c b/bacnet-stack/src/getevent.c new file mode 100644 index 00000000..6d0be0d9 --- /dev/null +++ b/bacnet-stack/src/getevent.c @@ -0,0 +1,477 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2009 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 "getevent.h" + +/* encode service */ +int getevent_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + 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_GET_EVENT_INFORMATION; + apdu_len = 4; + /* encode optional parameter */ + if (lastReceivedObjectIdentifier) { + len = + encode_context_object_id(&apdu[apdu_len], 0, + lastReceivedObjectIdentifier->type, + lastReceivedObjectIdentifier->instance); + apdu_len += len; + } + } + + return apdu_len; +} + +/* decode the service request only */ +int getevent_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + unsigned len = 0; + + /* check for value pointers */ + if (apdu_len && lastReceivedObjectIdentifier) { + /* Tag 0: Object ID - optional */ + if (!decode_is_context_tag(&apdu[len++], 0)) + return -1; + len += decode_object_id(&apdu[len], + &lastReceivedObjectIdentifier->type, + &lastReceivedObjectIdentifier->instance); + } + + return (int)len; +} + +int getevent_ack_encode_apdu_init( + uint8_t * apdu, + size_t max_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_GET_EVENT_INFORMATION; + apdu_len = 3; + /* service ack follows */ + /* Tag 0: listOfEventSummaries */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 0); + } + + return apdu_len; +} + +int getevent_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + BACNET_GET_EVENT_INFORMATION_DATA *event_data; + unsigned i = 0; /* counter */ + + if (apdu) { + event_data = get_event_data; + while (event_data) { + /* Tag 0: objectIdentifier */ + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, + event_data->objectIdentifier.type, + event_data->objectIdentifier.instance); + /* Tag 1: eventState */ + apdu_len += encode_context_enumerated(&apdu[apdu_len], 1, + event_data->eventState); + /* Tag 2: acknowledgedTransitions */ + apdu_len += encode_context_bitstring(&apdu[apdu_len], 2, + &event_data->acknowledgedTransitions); + /* Tag 3: eventTimeStamps */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 3); + for (i = 0; i < 3; i++) { + apdu_len += bacapp_encode_timestamp(&apdu[apdu_len], + &event_data->eventTimeStamps[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 3); + /* Tag 4: notifyType */ + apdu_len += encode_context_enumerated(&apdu[apdu_len], 4, + event_data->notifyType); + /* Tag 5: eventEnable */ + apdu_len += encode_context_bitstring(&apdu[apdu_len], 5, + &event_data->eventEnable); + /* Tag 6: eventPriorities */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 6); + for (i = 0; i < 3; i++) { + apdu_len += encode_application_unsigned(&apdu[apdu_len], + event_data->eventPriorities[i]); + } + apdu_len += encode_closing_tag(&apdu[apdu_len], 6); + event_data = event_data->next; + } + } + + return apdu_len; +} + +int getevent_ack_encode_apdu_end( + uint8_t * apdu, + size_t max_apdu, + bool moreEvents) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu_len += encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += encode_context_boolean(&apdu[apdu_len], 1, moreEvents); + } + + return apdu_len; +} + +int getevent_ack_decode_service_request( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool *moreEvents) +{ + uint8_t tag_number = 0; + uint32_t len_value = 0; + int len = 0; /* total length of decodes */ + uint32_t enum_value = 0; /* for decoding */ + BACNET_GET_EVENT_INFORMATION_DATA * event_data; + unsigned i = 0; /* counter */ + + /* FIXME: check apdu_len against the len during decode */ + event_data = get_event_data; + if (apdu && apdu_len && event_data && moreEvents) { + if (!decode_is_opening_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + while (event_data) { + /* Tag 0: objectIdentifier */ + if (decode_is_context_tag(&apdu[len], 0)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_object_id(&apdu[len], + &event_data->objectIdentifier.type, + &event_data->objectIdentifier.instance); + } else { + return -1; + } + /* Tag 1: eventState */ + if (decode_is_context_tag(&apdu[len], 1)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_enumerated(&apdu[len], len_value, + &enum_value); + event_data->eventState = enum_value; + } else { + return -1; + } + /* Tag 2: acknowledgedTransitions */ + if (decode_is_context_tag(&apdu[len], 2)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_bitstring(&apdu[len], len_value, + &event_data->acknowledgedTransitions); + } else { + return -1; + } + /* Tag 3: eventTimeStamps */ + if (decode_is_opening_tag_number(&apdu[len], 3)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + for (i = 0; i < 3; i++) { + len += bacapp_decode_timestamp(&apdu[len], + &event_data->eventTimeStamps[i]); + } + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 3)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + } else { + return -1; + } + /* Tag 4: notifyType */ + if (decode_is_context_tag(&apdu[len], 4)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_enumerated(&apdu[apdu_len], len_value, + &enum_value); + event_data->notifyType = enum_value; + } else { + return -1; + } + /* Tag 5: eventEnable */ + if (decode_is_context_tag(&apdu[len], 5)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_bitstring(&apdu[len], len_value, + &event_data->eventEnable); + } else { + return -1; + } + /* Tag 6: eventPriorities */ + if (decode_is_opening_tag_number(&apdu[len], 6)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + for (i = 0; i < 3; i++) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + len += decode_unsigned(&apdu[len], len_value, + &event_data->eventPriorities[i]); + } + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 6)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 0)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + event_data->next = NULL; + } + event_data = event_data->next; + } + if (decode_is_context_tag(&apdu[len], 1)) { + len += decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value); + *moreEvents = decode_boolean(len_value); + } else { + return -1; + } + } + + return len; +} + +#ifdef TEST +#include +#include +#include "ctest.h" + +int getevent_decode_apdu( + uint8_t * apdu, + unsigned apdu_len, + uint8_t * invoke_id, + BACNET_OBJECT_ID * lastReceivedObjectIdentifier) +{ + int len = 0; + unsigned offset = 0; + + if (!apdu) + return -1; + /* optional checking - most likely was already done prior to this call */ + if (apdu[0] != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) + return -1; + /* apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); */ + *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ + if (apdu[3] != SERVICE_CONFIRMED_GET_EVENT_INFORMATION) + return -1; + offset = 4; + + if (apdu_len > offset) { + len = getevent_decode_service_request(&apdu[offset], + apdu_len - offset, lastReceivedObjectIdentifier); + } + + return len; +} + +int getevent_ack_decode_apdu( + uint8_t * apdu, + int apdu_len, /* total length of the apdu */ + uint8_t * invoke_id, + BACNET_GET_EVENT_INFORMATION_DATA * get_event_data, + bool *moreEvents) +{ + 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_GET_EVENT_INFORMATION) + return -1; + offset = 3; + if (apdu_len > offset) { + len = + getevent_ack_decode_service_request(&apdu[offset], apdu_len - offset, + get_event_data, moreEvents); + } + + return len; +} + +void testGetEventInformationAck( + Test * pTest) +{ + uint8_t apdu[480] = { 0 }; + int len = 0; + int apdu_len = 0; + uint8_t invoke_id = 1; + uint8_t test_invoke_id = 0; + BACNET_GET_EVENT_INFORMATION_DATA event_data; + BACNET_GET_EVENT_INFORMATION_DATA test_event_data; + bool moreEvents = false; + bool test_moreEvents = false; + unsigned i = 0; + + event_data.objectIdentifier.type = OBJECT_BINARY_INPUT; + event_data.objectIdentifier.instance = 1; + event_data.eventState = EVENT_STATE_NORMAL; + bitstring_init(&event_data.acknowledgedTransitions); + bitstring_set_bit(&event_data.acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, false); + bitstring_set_bit(&event_data.acknowledgedTransitions, + TRANSITION_TO_FAULT, false); + bitstring_set_bit(&event_data.acknowledgedTransitions, + TRANSITION_TO_NORMAL, false); + for (i = 0; i < 3; i++) { + event_data.eventTimeStamps[i].tag = TIME_STAMP_SEQUENCE; + event_data.eventTimeStamps[i].value.sequenceNum = 0; + } + event_data.notifyType = NOTIFY_ALARM; + bitstring_init(&event_data.eventEnable); + bitstring_set_bit(&event_data.eventEnable, + TRANSITION_TO_OFFNORMAL, true); + bitstring_set_bit(&event_data.eventEnable, + TRANSITION_TO_FAULT, true); + bitstring_set_bit(&event_data.eventEnable, + TRANSITION_TO_NORMAL, true); + for (i = 0; i < 3; i++) { + event_data.eventPriorities[i] = 1; + } + event_data.next = NULL; + + len = getevent_ack_encode_apdu(&apdu[0], sizeof(apdu), invoke_id, + &event_data, moreEvents); + ct_test(pTest, len != 0); + ct_test(pTest, len != -1); + apdu_len = len; + len = getevent_ack_decode_apdu(&apdu[0], apdu_len, /* total length of the apdu */ + &test_invoke_id, &test_event_data, &test_moreEvents); + ct_test(pTest, len != -1); + ct_test(pTest, test_invoke_id == invoke_id); + + ct_test(pTest, + event_data.objectIdentifier.type == + test_event_data.objectIdentifier.type); + ct_test(pTest, + event_data.objectIdentifier.instance == + test_event_data.objectIdentifier.instance); + + ct_test(pTest, + event_data.eventState == + test_event_data.eventState); +} + +void testGetEventInformation( + 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_OBJECT_ID lastReceivedObjectIdentifier; + BACNET_OBJECT_ID test_lastReceivedObjectIdentifier; + + lastReceivedObjectIdentifier.type = OBJECT_BINARY_INPUT; + lastReceivedObjectIdentifier.instance = 12345; + len = getevent_encode_apdu(&apdu[0], invoke_id, + &lastReceivedObjectIdentifier); + ct_test(pTest, len != 0); + apdu_len = len; + + len = getevent_decode_apdu(&apdu[0], apdu_len, &test_invoke_id, + &test_lastReceivedObjectIdentifier); + ct_test(pTest, len != -1); + ct_test(pTest, + test_invoke_id == + invoke_id); + ct_test(pTest, + test_lastReceivedObjectIdentifier.type == + lastReceivedObjectIdentifier.type); + ct_test(pTest, + test_lastReceivedObjectIdentifier.instance == + lastReceivedObjectIdentifier.instance); + + return; +} + +#ifdef TEST_GET_EVENT_INFORMATION +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet GetEventInformation", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testGetEventInformation); + assert(rc); + rc = ct_addTestFunction(pTest, testGetEventInformationAck); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} +#endif +#endif /* TEST */ diff --git a/bacnet-stack/src/timestamp.c b/bacnet-stack/src/timestamp.c index 8e0089c5..432ff586 100644 --- a/bacnet-stack/src/timestamp.c +++ b/bacnet-stack/src/timestamp.c @@ -116,9 +116,8 @@ int bacapp_encode_context_timestamp( return apdu_len; } -int bacapp_decode_context_timestamp( +int bacapp_decode_timestamp( uint8_t * apdu, - uint8_t tag_number, BACNET_TIMESTAMP * value) { int len = 0; @@ -126,10 +125,7 @@ int bacapp_decode_context_timestamp( uint32_t len_value_type; uint32_t sequenceNum; - - if (decode_is_opening_tag_number(&apdu[len], tag_number)) { - len++; - + if (apdu) { section_len = decode_tag_number_and_value(&apdu[len], &value->tag, &len_value_type); @@ -177,10 +173,30 @@ int bacapp_decode_context_timestamp( default: return -1; } - if (decode_is_closing_tag_number(&apdu[len], tag_number)) { - len++; - } else { - return -1; + } + + return len; +} + +int bacapp_decode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value) +{ + int len = 0; + int section_len; + + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_len = bacapp_decode_timestamp(&apdu[len], value); + if (section_len > 0) { + len += section_len; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } else { + return -1; + } } } return len; diff --git a/bacnet-stack/test.mak b/bacnet-stack/test.mak index c3f4f17a..d5bf4023 100644 --- a/bacnet-stack/test.mak +++ b/bacnet-stack/test.mak @@ -1,7 +1,7 @@ LOGFILE = test.log all: abort address arf awf bacapp bacdcode bacerror bacint \ - bacstr cov crc datetime dcc fifo filename iam ihave \ + bacstr cov crc datetime dcc fifo getevent filename iam ihave \ indtext keylist key lso npdu rd reject ringbuf rp \ rpm sbuf timesync whohas whois wp event @@ -91,6 +91,11 @@ fifo: logfile test/fifo.mak ( ./test/fifo >> ${LOGFILE} ) make -C test -f fifo.mak clean +getevent: logfile test/getevent.mak + make -C test -f getevent.mak clean all + ( ./test/getevent >> ${LOGFILE} ) + make -C test -f getevent.mak clean + iam: logfile test/iam.mak make -C test -f iam.mak clean all ( ./test/iam >> ${LOGFILE} ) diff --git a/bacnet-stack/test/getevent.mak b/bacnet-stack/test/getevent.mak new file mode 100644 index 00000000..2e1297d4 --- /dev/null +++ b/bacnet-stack/test/getevent.mak @@ -0,0 +1,40 @@ +#Makefile to build test case +CC = gcc +SRC_DIR = ../src +INCLUDES = -I../include -I. +DEFINES = -DBIG_ENDIAN=0 -DTEST -DTEST_GET_EVENT_INFORMATION + +CFLAGS = -Wall $(INCLUDES) $(DEFINES) -g + +SRCS = $(SRC_DIR)/bacdcode.c \ + $(SRC_DIR)/bacint.c \ + $(SRC_DIR)/bacstr.c \ + $(SRC_DIR)/bacreal.c \ + $(SRC_DIR)/bacapp.c \ + $(SRC_DIR)/datetime.c \ + $(SRC_DIR)/bactext.c \ + $(SRC_DIR)/indtext.c \ + $(SRC_DIR)/timestamp.c \ + $(SRC_DIR)/getevent.c \ + ctest.c + +TARGET = getevent + +all: ${TARGET} + +OBJS = ${SRCS:.c=.o} + +${TARGET}: ${OBJS} + ${CC} -o $@ ${OBJS} + +.c.o: + ${CC} -c ${CFLAGS} $*.c -o $@ + +depend: + rm -f .depend + ${CC} -MM ${CFLAGS} *.c >> .depend + +clean: + rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini + +include: .depend