From 60ac0d0f8ada3618f2490897312527f56055ae62 Mon Sep 17 00:00:00 2001 From: minack Date: Tue, 14 Oct 2008 04:02:34 +0000 Subject: [PATCH] Added: -context specific decoders for all of the primitive times to go with the context specific encoders -unconfirmed/confirmed EventNotificationRequest structure/encoder/decoder -BACnetTimestamp structure/encoder/decoder -BACnetPropertyStates structure/encoder/decoder -BACnetDeviceObjectPropertyReference structure/encoder/decoder Changes: -decode_context_object_id::object_type changed to uint16_t -explicit casts have been added to some functions to remove compiler warnings -encode_bacnet_date::year behaviour has been changed slightly --- bacnet-stack/include/bacdcode.h | 58 +- bacnet-stack/include/bacdevobjpropref.h | 76 ++ bacnet-stack/include/bacpropstates.h | 105 ++ bacnet-stack/include/bacreal.h | 5 + bacnet-stack/include/bacstr.h | 2 +- bacnet-stack/include/datetime.h | 14 + bacnet-stack/include/event.h | 212 ++++ bacnet-stack/include/timestamp.h | 44 + bacnet-stack/src/bacdcode.c | 736 +++++++++++- bacnet-stack/src/bacdevobjpropref.c | 209 ++++ bacnet-stack/src/bacpropstates.c | 369 ++++++ bacnet-stack/src/bacreal.c | 19 + bacnet-stack/src/bacstr.c | 4 +- bacnet-stack/src/datetime.c | 137 ++- bacnet-stack/src/event.c | 1387 +++++++++++++++++++++++ bacnet-stack/src/timestamp.c | 249 ++++ 16 files changed, 3593 insertions(+), 33 deletions(-) create mode 100644 bacnet-stack/include/bacdevobjpropref.h create mode 100644 bacnet-stack/include/bacpropstates.h create mode 100644 bacnet-stack/include/event.h create mode 100644 bacnet-stack/include/timestamp.h create mode 100644 bacnet-stack/src/bacdevobjpropref.c create mode 100644 bacnet-stack/src/bacpropstates.c create mode 100644 bacnet-stack/src/event.c create mode 100644 bacnet-stack/src/timestamp.c diff --git a/bacnet-stack/include/bacdcode.h b/bacnet-stack/include/bacdcode.h index 7eefb65b..7663f38d 100644 --- a/bacnet-stack/include/bacdcode.h +++ b/bacnet-stack/include/bacdcode.h @@ -107,17 +107,27 @@ extern "C" { uint32_t len_value); int encode_context_boolean( uint8_t * apdu, - int tag_number, + uint8_t tag_number, bool boolean_value); bool decode_context_boolean( uint8_t * apdu); + int decode_context_boolean2( + uint8_t * apdu, + uint8_t tag_number, + bool *boolean_value); + /* from clause 20.2.10 Encoding of a Bit String Value */ /* returns the number of apdu bytes consumed */ int decode_bitstring( uint8_t * apdu, uint32_t len_value, BACNET_BIT_STRING * bit_string); + + int decode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string); /* returns the number of apdu bytes consumed */ int encode_bitstring( uint8_t * apdu, @@ -146,8 +156,15 @@ extern "C" { /* returns the number of apdu bytes consumed */ int decode_object_id( uint8_t * apdu, - int *object_type, + uint16_t * object_type, uint32_t * instance); + + int decode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + uint16_t *object_type, + uint32_t * instance); + int encode_bacnet_object_id( uint8_t * apdu, int object_type, @@ -179,6 +196,10 @@ extern "C" { uint8_t * apdu, uint32_t len_value, BACNET_OCTET_STRING * octet_string); + int decode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string); /* from clause 20.2.9 Encoding of a Character String Value */ @@ -198,6 +219,11 @@ extern "C" { uint8_t * apdu, uint32_t len_value, BACNET_CHARACTER_STRING * char_string); + int decode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string); + /* from clause 20.2.4 Encoding of an Unsigned Integer Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ @@ -216,6 +242,10 @@ extern "C" { uint8_t * apdu, uint32_t len_value, uint32_t * value); + int decode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t * value); /* from clause 20.2.5 Encoding of a Signed Integer Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ @@ -234,6 +264,11 @@ extern "C" { uint8_t * apdu, uint32_t len_value, int32_t * value); + int decode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t * value); + /* from clause 20.2.11 Encoding of an Enumerated Value */ /* and 20.2.1 General Rules for Encoding BACnet Tags */ @@ -242,6 +277,10 @@ extern "C" { uint8_t * apdu, uint32_t len_value, int *value); + int decode_context_enumerated( + uint8_t * apdu, + uint8_t tag_value, + int *value); int encode_bacnet_enumerated( uint8_t * apdu, int value); @@ -269,6 +308,14 @@ extern "C" { uint8_t * apdu, int tag_number, BACNET_TIME * btime); + int decode_application_time( + uint8_t * apdu, + BACNET_TIME * btime); + int decode_context_bacnet_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime); + /* BACnet Date */ /* year = years since 1900 */ @@ -292,6 +339,13 @@ extern "C" { int decode_date( uint8_t * apdu, BACNET_DATE * bdate); + int decode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate); + int decode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate); /* from clause 20.1.2.4 max-segments-accepted */ /* and clause 20.1.2.5 max-APDU-length-accepted */ diff --git a/bacnet-stack/include/bacdevobjpropref.h b/bacnet-stack/include/bacdevobjpropref.h new file mode 100644 index 00000000..3cf08874 --- /dev/null +++ b/bacnet-stack/include/bacdevobjpropref.h @@ -0,0 +1,76 @@ + +/*####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 _BAC_DEV_PROP_REF_H_ +#define _BAC_DEV_PROP_REF_H_ + + +typedef struct { + BACNET_OBJECT_ID objectIdentifier; + BACNET_PROPERTY_ID propertyIdentifier; + uint32_t arrayIndex; + BACNET_OBJECT_ID deviceIndentifier; +} BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int bacapp_encode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + +int bacapp_encode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + +int bacapp_decode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + +int bacapp_decode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif //_BAC_DEV_PROP_REF_H_ + diff --git a/bacnet-stack/include/bacpropstates.h b/bacnet-stack/include/bacpropstates.h new file mode 100644 index 00000000..66cd83a3 --- /dev/null +++ b/bacnet-stack/include/bacpropstates.h @@ -0,0 +1,105 @@ + +/*####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 _BAC_PROP_STATES_H_ +#define _BAC_PROP_STATES_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include +#include "timestamp.h" + +typedef enum { + BOOLEAN_VALUE, + BINARY_VALUE, + EVENT_TYPE, + POLARITY, + PROGRAM_CHANGE, + PROGRAM_STATE, + REASON_FOR_HALT, + RELIABILITY, + STATE, + SYSTEM_STATUS, + UNITS, + UNSIGNED_VALUE, + LIFE_SAFETY_MODE, + LIFE_SAFETY_STATE, +} BACNET_PROPERTY_STATE_TYPE; + +typedef struct { + BACNET_PROPERTY_STATE_TYPE tag; + union { + bool booleanValue; + BACNET_BINARY_PV binaryValue; + BACNET_EVENT_TYPE eventType; + BACNET_POLARITY polarity; + BACNET_PROGRAM_REQUEST programChange; + BACNET_PROGRAM_STATE programState; + BACNET_PROGRAM_ERROR programError; + BACNET_RELIABILITY reliability; + BACNET_EVENT_STATE state; + BACNET_DEVICE_STATUS systemStatus; + BACNET_ENGINEERING_UNITS units; + uint32_t unsignedValue; + BACNET_LIFE_SAFETY_MODE lifeSafetyMode; + BACNET_LIFE_SAFETY_STATE lifeSafetyState; + } state; +} BACNET_PROPERTY_STATE; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +int bacapp_decode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value); + +int bacapp_decode_context_property_state( + uint8_t * apdu, + uint8_t tag_number, + BACNET_PROPERTY_STATE * value); + +int bacapp_encode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif // _BAC_PROP_STATES_H_ diff --git a/bacnet-stack/include/bacreal.h b/bacnet-stack/include/bacreal.h index d2713394..a0081685 100644 --- a/bacnet-stack/include/bacreal.h +++ b/bacnet-stack/include/bacreal.h @@ -45,6 +45,11 @@ extern "C" { int decode_real( uint8_t * apdu, float *real_value); + + int decode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float *real_value); int encode_bacnet_real( float value, uint8_t * apdu); diff --git a/bacnet-stack/include/bacstr.h b/bacnet-stack/include/bacstr.h index f00659a3..95c772b3 100644 --- a/bacnet-stack/include/bacstr.h +++ b/bacnet-stack/include/bacstr.h @@ -79,7 +79,7 @@ extern "C" { 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( + uint8_t bitstring_bytes_used( BACNET_BIT_STRING * bit_string); uint8_t bitstring_bits_capacity( BACNET_BIT_STRING * bit_string); diff --git a/bacnet-stack/include/datetime.h b/bacnet-stack/include/datetime.h index 43907289..491eb064 100644 --- a/bacnet-stack/include/datetime.h +++ b/bacnet-stack/include/datetime.h @@ -138,6 +138,20 @@ extern "C" { void datetime_time_wildcard_set( BACNET_TIME * btime); + int bacapp_encode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value); + + int bacapp_decode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value); + + int bacapp_decode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/bacnet-stack/include/event.h b/bacnet-stack/include/event.h new file mode 100644 index 00000000..f1cbe128 --- /dev/null +++ b/bacnet-stack/include/event.h @@ -0,0 +1,212 @@ +/*####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 BACNET_EVENT_H_ +#define BACNET_EVENT_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include "timestamp.h" +#include "bacpropstates.h" +#include "bacdevobjpropref.h" + +typedef enum { + CHANGE_OF_VALUE_BITS, + CHANGE_OF_VALUE_REAL +} CHANGE_OF_VALUE_TYPE; + +/* +** Based on UnconfirmedEventNotification-Request +*/ + +typedef struct BACnet_Event_Notification_Data { + uint32_t processIdentifier; + BACNET_OBJECT_ID initiatingObjectIdentifier; + BACNET_OBJECT_ID eventObjectIdentifier; + BACNET_TIMESTAMP timeStamp; + uint32_t notificationClass; + uint8_t priority; + BACNET_EVENT_TYPE eventType; + BACNET_CHARACTER_STRING* messageText; /* OPTIONAL - Set to NULL if not being used */ + BACNET_NOTIFY_TYPE notifyType; + bool ackRequired; + BACNET_EVENT_STATE fromState; + BACNET_EVENT_STATE toState; + /* + ** Each of these structures in the union maps to a particular eventtype + ** Based on BACnetNotificationParameters + */ + + union { + /* + ** EVENT_CHANGE_OF_BITSTRING + */ + struct { + BACNET_BIT_STRING referencedBitString; + BACNET_BIT_STRING statusFlags; + } changeOfBitstring; + /* + ** EVENT_CHANGE_OF_STATE + */ + struct { + BACNET_PROPERTY_STATE newState; + BACNET_BIT_STRING statusFlags; + } changeOfState; + /* + ** EVENT_CHANGE_OF_VALUE + */ + struct { + union { + BACNET_BIT_STRING changedBits; + float changeValue; + } newValue; + CHANGE_OF_VALUE_TYPE tag; + BACNET_BIT_STRING statusFlags; + } changeOfValue; + /* + ** EVENT_COMMAND_FAILURE + ** + ** Not Supported! + */ + /* + ** EVENT_FLOATING_LIMIT + */ + struct { + float referenceValue; + BACNET_BIT_STRING statusFlags; + float setPointValue; + float errorLimit; + } floatingLimit; + /* + ** EVENT_OUT_OF_RANGE + */ + struct { + float exceedingValue; + BACNET_BIT_STRING statusFlags; + float deadband; + float exceededLimit; + } outOfRange; + /* + ** EVENT_CHANGE_OF_LIFE_SAFETY + */ + struct { + BACNET_LIFE_SAFETY_STATE newState; + BACNET_LIFE_SAFETY_MODE newMode; + BACNET_BIT_STRING statusFlags; + BACNET_LIFE_SAFETY_OPERATION operationExpected; + } changeOfLifeSafety; + /* + ** EVENT_EXTENDED + ** + ** Not Supported! + */ + /* + ** EVENT_BUFFER_READY + */ + struct { + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE bufferProperty; + uint32_t previousNotification; + uint32_t currentNotification; + } bufferReady; + /* + ** EVENT_UNSIGNED_RANGE + */ + struct { + uint32_t exceedingValue; + BACNET_BIT_STRING statusFlags; + uint32_t exceededLimit; + } unsignedRange; + } notificationParams; +} BACNET_EVENT_NOTIFICATION_DATA; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/*************************************************** +** +** Creates a Confirmed Event Notification APDU +** +****************************************************/ +int cevent_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Creates an Unconfirmed Event Notification APDU +** +****************************************************/ +int uevent_notify_encode_apdu( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Encodes the service data part of Event Notification +** +****************************************************/ +int event_notify_encode_service_request( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Decodes the service data part of Event Notification +** +****************************************************/ +int event_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_EVENT_NOTIFICATION_DATA * data); + +/*************************************************** +** +** Sends an Unconfirmed Event Notifcation to a dest +** +****************************************************/ +int uevent_notify_send( + uint8_t * buffer, + BACNET_EVENT_NOTIFICATION_DATA * data, + BACNET_ADDRESS *dest); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* BACNET_EVENT_H_ */ diff --git a/bacnet-stack/include/timestamp.h b/bacnet-stack/include/timestamp.h new file mode 100644 index 00000000..74ccdd43 --- /dev/null +++ b/bacnet-stack/include/timestamp.h @@ -0,0 +1,44 @@ +#ifndef _TIMESTAMP_H_ +#define _TIMESTAMP_H_ + +#include "bacdcode.h" + +typedef enum { + TIME_STAMP_TIME = 0, + TIME_STAMP_SEQUENCE = 1, + TIME_STAMP_DATETIME = 2, +} BACNET_TIMESTAMP_TAG; + +typedef uint8_t TYPE_BACNET_TIMESTAMP_TYPE; + +typedef struct { + TYPE_BACNET_TIMESTAMP_TYPE tag; + union { + BACNET_TIME time; + uint16_t sequenceNum; + BACNET_DATE_TIME dateTime; + } value; +} BACNET_TIMESTAMP; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + + +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, + BACNET_TIMESTAMP * value); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif diff --git a/bacnet-stack/src/bacdcode.c b/bacnet-stack/src/bacdcode.c index c5cbd360..4f4a48a3 100644 --- a/bacnet-stack/src/bacdcode.c +++ b/bacnet-stack/src/bacdcode.c @@ -71,6 +71,24 @@ B'1110' reserved by ASHRAE B'1111' reserved by ASHRAE */ + +/* Encoding of BACNET Length/Value/Type tag + From clause 20.2.1.3.1 + + B'000' interpreted as Value = FALSE if application class == BOOLEAN + B'001' interpreted as Value = TRUE if application class == BOOLEAN + + B'000' interpreted as Length = 0 if application class != BOOLEAN + B'001' interpreted as Length = 1 + B'010' interpreted as Length = 2 + B'011' interpreted as Length = 3 + B'100' interpreted as Length = 4 + B'101' interpreted as Length > 4 + B'110' interpreted as Type = Opening Tag + B'111' interpreted as Type = Closing Tag +*/ + + /* from clause 20.1.2.4 max-segments-accepted */ /* and clause 20.1.2.5 max-APDU-length-accepted */ /* returns the encoded octet */ @@ -285,7 +303,7 @@ int encode_closing_tag( static bool decode_is_extended_tag_number( uint8_t * apdu) { - return ((apdu[0] & 0xF0) == 0xF0); + return (bool)((apdu[0] & 0xF0) == 0xF0); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -293,7 +311,7 @@ static bool decode_is_extended_tag_number( static bool decode_is_extended_value( uint8_t * apdu) { - return ((apdu[0] & 0x07) == 5); + return (bool)((apdu[0] & 0x07) == 5); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -301,7 +319,7 @@ static bool decode_is_extended_value( bool decode_is_context_specific( uint8_t * apdu) { - return ((apdu[0] & BIT3) == BIT3); + return (bool)((apdu[0] & BIT3) == BIT3); } int decode_tag_number( @@ -319,7 +337,7 @@ int decode_tag_number( len++; } else { if (tag_number) { - *tag_number = (apdu[0] >> 4); + *tag_number = (uint8_t)(apdu[0] >> 4); } } @@ -329,7 +347,7 @@ int decode_tag_number( bool decode_is_opening_tag( uint8_t * apdu) { - return ((apdu[0] & 0x07) == 6); + return (bool)((apdu[0] & 0x07) == 6); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -337,7 +355,7 @@ bool decode_is_opening_tag( bool decode_is_closing_tag( uint8_t * apdu) { - return ((apdu[0] & 0x07) == 7); + return (bool)((apdu[0] & 0x07) == 7); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -398,10 +416,26 @@ bool decode_is_context_tag( 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)); + return (bool)(context_specific && (my_tag_number == tag_number)); +} + +bool decode_is_context_tag_with_length( + uint8_t * apdu, + uint8_t tag_number, + int * tag_length) +{ + uint8_t my_tag_number = 0; + bool context_specific = false; + + + context_specific = decode_is_context_specific(apdu); + *tag_length = decode_tag_number(apdu, &my_tag_number); + + return (bool)(context_specific && (my_tag_number == tag_number)); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -413,10 +447,10 @@ bool decode_is_opening_tag_number( uint8_t my_tag_number = 0; bool opening_tag = false; - opening_tag = ((apdu[0] & 0x07) == 6); + opening_tag = (bool)((apdu[0] & 0x07) == 6); decode_tag_number(apdu, &my_tag_number); - return (opening_tag && (my_tag_number == tag_number)); + return (bool)(opening_tag && (my_tag_number == tag_number)); } /* from clause 20.2.1.3.2 Constructed Data */ @@ -428,10 +462,10 @@ bool decode_is_closing_tag_number( uint8_t my_tag_number = 0; bool closing_tag = false; - closing_tag = ((apdu[0] & 0x07) == 7); + closing_tag = (bool)((apdu[0] & 0x07) == 7); decode_tag_number(apdu, &my_tag_number); - return (closing_tag && (my_tag_number == tag_number)); + return (bool)(closing_tag && (my_tag_number == tag_number)); } /* from clause 20.2.3 Encoding of a Boolean Value */ @@ -456,13 +490,13 @@ int encode_application_boolean( /* context tagged is encoded differently */ int encode_context_boolean( uint8_t * apdu, - int tag_number, + uint8_t 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; + apdu[len] = (bool)(boolean_value ? 1 : 0); len++; return len; @@ -480,6 +514,29 @@ bool decode_context_boolean( return boolean_value; } +int decode_context_boolean2( + uint8_t * apdu, + uint8_t tag_number, + bool *boolean_value) +{ + int len = 0; + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + if (apdu[len]) { + *boolean_value = true; + } + else + { + *boolean_value = false; + } + len++; + } + else + { + len = -1; + } + return len; +} + /* 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 */ @@ -567,7 +624,7 @@ int decode_bitstring( bitstring_set_octet(bit_string, (uint8_t) i, byte_reverse_bits(apdu[len++])); } - unused_bits = apdu[0] & 0x07; + unused_bits = (uint8_t)(apdu[0] & 0x07); bitstring_set_bits_used(bit_string, (uint8_t) bytes_used, unused_bits); } @@ -576,6 +633,27 @@ int decode_bitstring( return len; } +int decode_context_bitstring( + uint8_t * apdu, + uint8_t tag_number, + BACNET_BIT_STRING * bit_string) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_bitstring(&apdu[len], len_value, bit_string); + } + else + { + len = -1; + } + return len; +} + + /* from clause 20.2.10 Encoding of a Bit String Value */ /* returns the number of apdu bytes consumed */ int encode_bitstring( @@ -593,9 +671,9 @@ int encode_bitstring( } else { used_bytes = bitstring_bytes_used(bit_string); remaining_used_bits = - bitstring_bits_used(bit_string) - ((used_bytes - 1) * 8); + (uint8_t)(bitstring_bits_used(bit_string) - ((used_bytes - 1) * 8)); /* number of unused bits in the subsequent final octet */ - apdu[len++] = 8 - remaining_used_bits; + apdu[len++] = (uint8_t)(8 - remaining_used_bits); for (i = 0; i < used_bytes; i++) { apdu[len++] = byte_reverse_bits(bitstring_octet(bit_string, i)); } @@ -650,12 +728,30 @@ int decode_object_id( int len = 0; len = decode_unsigned32(apdu, &value); - *object_type = ((value >> BACNET_INSTANCE_BITS) & BACNET_MAX_OBJECT); + *object_type = (uint16_t)(((value >> BACNET_INSTANCE_BITS) & BACNET_MAX_OBJECT)); *instance = (value & BACNET_MAX_INSTANCE); return len; } +int decode_context_object_id( + uint8_t * apdu, + uint8_t tag_number, + uint16_t * object_type, + uint32_t * instance) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_object_id(&apdu[len], object_type, instance); + } + else + { + len = -1; + } + 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( @@ -807,6 +903,34 @@ int decode_octet_string( return len; } +int decode_context_octet_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_OCTET_STRING * octet_string) +{ + int len = 0; /* return value */ + bool status = false; + uint32_t len_value = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) + { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + + status = octetstring_init(octet_string, &apdu[len], len_value); + + if (status) { + len += len_value; + } + } + else + { + len = -1; + } + + 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( @@ -889,6 +1013,35 @@ int decode_character_string( return len; } +int decode_context_character_string( + uint8_t * apdu, + uint8_t tag_number, + BACNET_CHARACTER_STRING * char_string) +{ + int len = 0; /* return value */ + bool status = false; + uint32_t len_value = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) + { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + + status = + characterstring_init(char_string, apdu[len], (char *) &apdu[len+1], + len_value - 1); + if (status) { + len += len_value; + } + } + else + { + len = -1; + } + + 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 */ @@ -923,6 +1076,27 @@ int decode_unsigned( return len_value; } +int decode_context_unsigned( + uint8_t * apdu, + uint8_t tag_number, + uint32_t * value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_unsigned(&apdu[len], len_value, value); + } + else + { + len = -1; + } + 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 */ @@ -1002,6 +1176,27 @@ int decode_enumerated( return len; } +int decode_context_enumerated( + uint8_t * apdu, + uint8_t tag_value, + int *value) +{ + int len = 0; + uint8_t tag_number; + uint32_t len_value; + + if (decode_is_context_tag(&apdu[len], tag_value)) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_enumerated(&apdu[len], len_value, value); + } + else + { + len = -1; + } + 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 */ @@ -1081,6 +1276,26 @@ int decode_signed( return len_value; } +int decode_context_signed( + uint8_t * apdu, + uint8_t tag_number, + int32_t * value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_signed(&apdu[len], len_value, value); + } + else + { + len = -1; + } + 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 */ @@ -1281,6 +1496,45 @@ int decode_bacnet_time( return 4; } +int decode_application_time( + uint8_t * apdu, + BACNET_TIME * btime) +{ + int len = 0; + uint8_t tag_number; + decode_tag_number(&apdu[len], &tag_number); + + if ( tag_number == BACNET_APPLICATION_TAG_TIME ) + { + len++; + len += decode_bacnet_time(&apdu[len], btime); + } + else + { + len = -1; + } + return len; +} + + +int decode_context_bacnet_time( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIME * btime) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_bacnet_time(&apdu[len], btime); + } + else + { + len = -1; + } + return len; +} + + /* BACnet Date */ /* year = years since 1900 */ /* month 1=Jan */ @@ -1295,14 +1549,23 @@ int encode_bacnet_date( BACNET_DATE * bdate) { /* allow 2 digit years */ - if (bdate->year < 1900) { - if (bdate->year <= 38) { - bdate->year += 2000; - } else { - bdate->year += 1900; - } + if (bdate->year >= 1900) { + apdu[0] = (uint8_t)(bdate->year - 1900); } - apdu[0] = bdate->year - 1900; + else if ( bdate->year < 0x100 ) + { + apdu[0] = (uint8_t)bdate->year; + + } + else + { + /* + ** Don't try and guess what the user meant here. Just fail + */ + return -1; + } + + apdu[1] = bdate->month; apdu[2] = bdate->day; apdu[3] = bdate->wday; @@ -1310,6 +1573,7 @@ int encode_bacnet_date( 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 */ @@ -1353,7 +1617,7 @@ int decode_date( uint8_t * apdu, BACNET_DATE * bdate) { - bdate->year = apdu[0] + 1900; + bdate->year = (uint16_t)(apdu[0] + 1900); bdate->month = apdu[1]; bdate->day = apdu[2]; bdate->wday = apdu[3]; @@ -1361,6 +1625,45 @@ int decode_date( return 4; } +int decode_application_date( + uint8_t * apdu, + BACNET_DATE * bdate) +{ + int len = 0; + uint8_t tag_number; + decode_tag_number(&apdu[len], &tag_number); + + if ( tag_number == BACNET_APPLICATION_TAG_DATE ) + { + len++; + len += decode_date(&apdu[len], bdate); + } + else + { + len = -1; + } + return len; +} + +int decode_context_date( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE * bdate) +{ + int len = 0; + + if (decode_is_context_tag_with_length(&apdu[len], tag_number, &len)) { + len += decode_date(&apdu[len], bdate); + } + else + { + len = -1; + } + return len; +} + + + /* returns the number of apdu bytes consumed */ int encode_simple_ack( uint8_t * apdu, @@ -1990,6 +2293,358 @@ void testBACDCodeBitString( } } +void testUnsignedContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + /* 32 bit number */ + uint32_t in = 0xdeadbeef; + uint32_t out; + + outLen2 = decode_context_unsigned(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == -1); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 8 bit number */ + in = 0xde; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 4 bit number */ + in = 0xd; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 2 bit number */ + in = 0x2; + inLen = encode_context_unsigned(apdu, 10, in); + outLen = decode_context_unsigned(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); +} + +void testSignedContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + /* 32 bit number */ + int32_t in = 0xdeadbeef; + int32_t out; + + outLen2 = decode_context_signed(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == -1); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 8 bit number */ + in = 0xde; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 4 bit number */ + in = 0xd; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 2 bit number */ + in = 0x2; + inLen = encode_context_signed(apdu, 10, in); + outLen = decode_context_signed(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); +} + +void testEnumeratedContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + /* 32 bit number */ + int32_t in = 0xdeadbeef; + int32_t out; + + outLen2 = decode_context_enumerated(apdu, 9, &out); + + in = 0xdeadbeef; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == -1); + + /* 16 bit number */ + in = 0xdead; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 8 bit number */ + in = 0xde; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 4 bit number */ + in = 0xd; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + + /* 2 bit number */ + in = 0x2; + inLen = encode_context_enumerated(apdu, 10, in); + outLen = decode_context_enumerated(apdu, 10, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); +} + +void testFloatContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + /* 32 bit number */ + float in; + float out; + + + in = 0.1234f; + inLen = encode_context_real(apdu, 10, in); + outLen = decode_context_real(apdu, 10, &out); + outLen2 = decode_context_real(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); + ct_test(pTest, outLen2 == -1); + + in = 0.0f; + inLen = encode_context_real(apdu, 10, in); + outLen = decode_context_real(apdu, 10, &out); + outLen2 = decode_context_real(apdu, 9, &out); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in == out); +} + +void testObjectIDContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + /* 32 bit number */ + uint16_t in_type; + uint32_t in_id; + + uint16_t out_type; + uint32_t out_id; + + in_type = 0xde; + in_id = 0xbeef; + + inLen = encode_context_object_id(apdu, 10, in_type, in_id); + outLen = decode_context_object_id(apdu, 10, &out_type, &out_id); + outLen2 = decode_context_object_id(apdu, 9, &out_type, &out_id); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, in_type == out_type); + ct_test(pTest, in_id == out_id); + ct_test(pTest, outLen2 == -1); + +} + +void testCharacterStringContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + BACNET_CHARACTER_STRING in; + BACNET_CHARACTER_STRING out; + + characterstring_init_ansi(&in, "This is a test"); + + inLen = encode_context_character_string(apdu, 10, &in); + outLen= decode_context_character_string(apdu, 10, &out); + outLen2= decode_context_character_string(apdu, 9, &out); + + ct_test(pTest, outLen2 == -1); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, in.encoding == out.encoding); + ct_test(pTest, strcmp(in.value, out.value) == 0); +} + +void testBitStringContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + BACNET_BIT_STRING in; + BACNET_BIT_STRING out; + + bitstring_init(&in); + bitstring_set_bit(&in, 1, true); + bitstring_set_bit(&in, 3, true); + bitstring_set_bit(&in, 6, true); + bitstring_set_bit(&in, 10, false); + bitstring_set_bit(&in, 11, true); + bitstring_set_bit(&in, 12, false); + + inLen = encode_context_bitstring(apdu, 10, &in); + outLen= decode_context_bitstring(apdu, 10, &out); + outLen2= decode_context_bitstring(apdu, 9, &out); + + ct_test(pTest, outLen2 == -1); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.bits_used == out.bits_used); + ct_test(pTest, memcmp(in.value, out.value, MAX_BITSTRING_BYTES) == 0); + +} + +void testOctetStringContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + BACNET_OCTET_STRING in; + BACNET_OCTET_STRING out; + + uint8_t initData[] = {0xde,0xad,0xbe,0xef}; + + octetstring_init(&in, initData, sizeof(initData)); + + inLen = encode_context_octet_string(apdu, 10, &in); + outLen= decode_context_octet_string(apdu, 10, &out); + outLen2= decode_context_octet_string(apdu, 9, &out); + + ct_test(pTest, outLen2 == -1); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.length == out.length); + ct_test(pTest, memcmp(in.value, out.value, MAX_APDU - 6) == 0); + +} + +void testTimeContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + BACNET_TIME in; + BACNET_TIME out; + + in.hour = 10; + in.hundredths = 20; + in.min = 30; + in.sec = 40; + + inLen = encode_context_time(apdu, 10, &in); + outLen= decode_context_bacnet_time(apdu, 10, &out); + outLen2= decode_context_bacnet_time(apdu, 9, &out); + + ct_test(pTest, outLen2 == -1); + ct_test(pTest, inLen == outLen); + ct_test(pTest, in.hour == out.hour); + ct_test(pTest, in.hundredths == out.hundredths); + ct_test(pTest, in.min == out.min); + ct_test(pTest, in.sec == out.sec); +} + +void testDateContextDecodes(Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + int inLen; + int outLen; + int outLen2; + + BACNET_DATE in; + BACNET_DATE out; + + in.day = 3; + in.month = 10; + in.wday = 5; + in.year = 1945; + + inLen = encode_context_date(apdu, 10, &in); + outLen= decode_context_date(apdu, 10, &out); + outLen2= decode_context_date(apdu, 9, &out); + + 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); +} + + #ifdef TEST_DECODE int main( void) @@ -2023,6 +2678,37 @@ int main( assert(rc); rc = ct_addTestFunction(pTest, testBACDCodeBitString); assert(rc); + + rc = ct_addTestFunction(pTest, testUnsignedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testSignedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testEnumeratedContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testCharacterStringContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testFloatContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testObjectIDContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testBitStringContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimeContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testDateContextDecodes); + assert(rc); + + rc = ct_addTestFunction(pTest, testOctetStringContextDecodes); + assert(rc); + /* configure output */ ct_setStream(pTest, stdout); ct_run(pTest); diff --git a/bacnet-stack/src/bacdevobjpropref.c b/bacnet-stack/src/bacdevobjpropref.c new file mode 100644 index 00000000..65391d7b --- /dev/null +++ b/bacnet-stack/src/bacdevobjpropref.c @@ -0,0 +1,209 @@ +#include +#include "bacdcode.h" +#include "npdu.h" +#include "device.h" +#include "datalink.h" +#include "timestamp.h" +#include "bacdevobjpropref.h" + +int bacapp_encode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = bacapp_encode_device_obj_property_ref(&apdu[apdu_len], value); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + return apdu_len; +} + +int bacapp_encode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + len = encode_context_object_id(&apdu[apdu_len], 0, + value->objectIdentifier.type, + value->objectIdentifier.instance); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 1, + value->propertyIdentifier); + apdu_len += len; + + if ( value->arrayIndex > 0 ) + { + len = encode_context_unsigned(&apdu[apdu_len], 2, + value->arrayIndex); + apdu_len += len; + } + len = encode_context_object_id(&apdu[apdu_len], 3, + value->deviceIndentifier.type, + value->deviceIndentifier.instance); + apdu_len += len; + + return apdu_len; +} + +int bacapp_decode_device_obj_property_ref( + uint8_t * apdu, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len; + int apdu_len = 0; + + if ( -1 == ( len = decode_context_object_id(&apdu[apdu_len], 0, + &value->objectIdentifier.type, + &value->objectIdentifier.instance)) ) + { + return -1; + } + apdu_len += len; + + if ( -1 == ( len = decode_context_enumerated(&apdu[apdu_len], 1, + &value->propertyIdentifier))) + { + return -1; + } + apdu_len += len; + + if ( decode_is_context_tag(&apdu[apdu_len], 2 )) + { + if ( -1 == ( len = decode_context_unsigned(&apdu[apdu_len], 2, + &value->arrayIndex))) + { + return -1; + } + apdu_len += len; + } + else + { + value->arrayIndex = 0; + } + + if ( decode_is_context_tag(&apdu[apdu_len], 3 )) + { + if ( -1 == ( len = decode_context_object_id(&apdu[apdu_len], 3, + &value->deviceIndentifier.type, + &value->deviceIndentifier.instance)) ) + { + return -1; + } + apdu_len += len; + } + else + { + value->deviceIndentifier.instance = 0; + value->deviceIndentifier.type = 0; + } + + return apdu_len; +} + +int bacapp_decode_context_device_obj_property_ref( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = bacapp_decode_device_obj_property_ref( + &apdu[len], value); + + if ( section_length == -1 ) + { + len = -1; + } + else + { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } + else + { + len = -1; + } + } + } + else + { + len = -1; + } + return len; +} + +#ifdef TEST + +void testDevIdPropRef( + Test * pTest) +{ + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE inData; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE outData; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + + inData.objectIdentifier.instance = 0x1234; + inData.objectIdentifier.type = 15; + + inData.propertyIdentifier = 25; + + inData.arrayIndex = 0x5678; + + inData.deviceIndentifier.instance = 0x4343; + inData.deviceIndentifier.type = 28; + + inLen = bacapp_encode_device_obj_property_ref(buffer, &inData); + outLen = bacapp_decode_device_obj_property_ref(buffer, &outData); + + ct_test(pTest, outLen == inLen); + + ct_test(pTest, inData.objectIdentifier.instance == outData.objectIdentifier.instance); + ct_test(pTest, inData.objectIdentifier.type == outData.objectIdentifier.type); + + ct_test(pTest, inData.propertyIdentifier == outData.propertyIdentifier); + + ct_test(pTest, inData.arrayIndex == outData.arrayIndex); + + ct_test(pTest, inData.deviceIndentifier.instance == outData.deviceIndentifier.instance); + ct_test(pTest, inData.deviceIndentifier.type == outData.deviceIndentifier.type); +} +#ifdef TEST_DEV_ID_PROP_REF + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Prop Ref", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testDevIdPropRef); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif // TEST_DEV_ID_PROP_REF +#endif // TEST diff --git a/bacnet-stack/src/bacpropstates.c b/bacnet-stack/src/bacpropstates.c new file mode 100644 index 00000000..eb7568f4 --- /dev/null +++ b/bacnet-stack/src/bacpropstates.c @@ -0,0 +1,369 @@ +#include +#include "bacdcode.h" +#include "npdu.h" +#include "device.h" +#include "datalink.h" +#include "timestamp.h" +#include "bacpropstates.h" + +int bacapp_decode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; + uint32_t len_value_type; + int section_length; + + + section_length = + decode_tag_number_and_value(&apdu[len], (uint8_t*)&value->tag, + &len_value_type); + + if ( -1 == section_length ) + { + return -1; + } + + len += section_length; + switch(value->tag) + { + case BOOLEAN_VALUE: + value->state.booleanValue = decode_boolean(len_value_type); + break; + + case BINARY_VALUE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.binaryValue))) + { + return -1; + } + break; + + case EVENT_TYPE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.eventType))) + { + return -1; + } + break; + + case POLARITY: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.polarity))) + { + return -1; + } + break; + + case PROGRAM_CHANGE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.programChange))) + { + return -1; + } + break; + + case PROGRAM_STATE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.programState))) + { + return -1; + } + break; + + case REASON_FOR_HALT: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.programError))) + { + return -1; + } + break; + + case RELIABILITY: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.reliability))) + { + return -1; + } + break; + + case STATE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.state))) + { + return -1; + } + break; + + case SYSTEM_STATUS: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.systemStatus))) + { + return -1; + } + break; + + case UNITS: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.units))) + { + return -1; + } + break; + + case UNSIGNED_VALUE: + if ( -1 == ( section_length = decode_unsigned(&apdu[len], len_value_type, &value->state.unsignedValue))) + { + return -1; + } + break; + + case LIFE_SAFETY_MODE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.lifeSafetyMode))) + { + return -1; + } + break; + + case LIFE_SAFETY_STATE: + if ( -1 == ( section_length = decode_enumerated(&apdu[len], len_value_type, &value->state.lifeSafetyState))) + { + return -1; + } + break; + + default: + return -1; + } + len += section_length; + + return len; +} + +int bacapp_decode_context_property_state( + uint8_t * apdu, + uint8_t tag_number, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; + int section_length; + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + section_length = bacapp_decode_property_state( + &apdu[len], value); + + if ( section_length == -1 ) + { + len = -1; + } + else + { + len += section_length; + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } + else + { + len = -1; + } + } + } + else + { + len = -1; + } + return len; +} + +int bacapp_encode_property_state( + uint8_t * apdu, + BACNET_PROPERTY_STATE * value) +{ + int len = 0; /* length of each encoding */ + if (value && apdu) { + switch(value->tag) + { + case BOOLEAN_VALUE: + len = encode_context_boolean(&apdu[0], 0, value->state.booleanValue); + break; + + case BINARY_VALUE: + len = encode_context_enumerated(&apdu[0], 1, value->state.binaryValue); + break; + + case EVENT_TYPE: + len = encode_context_enumerated(&apdu[0], 2, value->state.eventType); + break; + + case POLARITY: + len = encode_context_enumerated(&apdu[0], 3, value->state.polarity); + break; + + case PROGRAM_CHANGE: + len = encode_context_enumerated(&apdu[0], 4, value->state.programChange); + break; + + case PROGRAM_STATE: + len = encode_context_enumerated(&apdu[0], 5, value->state.programState); + break; + + case REASON_FOR_HALT: + len = encode_context_enumerated(&apdu[0], 6, value->state.programError); + break; + + case RELIABILITY: + len = encode_context_enumerated(&apdu[0], 7, value->state.reliability); + break; + + case STATE: + len = encode_context_enumerated(&apdu[0], 8, value->state.state); + break; + + case SYSTEM_STATUS: + len = encode_context_enumerated(&apdu[0], 9, value->state.systemStatus); + break; + + case UNITS: + len = encode_context_enumerated(&apdu[0], 10, value->state.units); + break; + + case UNSIGNED_VALUE: + len = encode_context_unsigned(&apdu[0], 11, value->state.unsignedValue); + break; + + case LIFE_SAFETY_MODE: + len = encode_context_enumerated(&apdu[0], 12, value->state.lifeSafetyMode); + break; + + case LIFE_SAFETY_STATE: + len = encode_context_enumerated(&apdu[0], 13, value->state.lifeSafetyState); + break; + + default: + assert(0); + break; + } + } + return len; +} + +#ifdef TEST + +void testPropStates( + Test * pTest) +{ + BACNET_PROPERTY_STATE inData; + BACNET_PROPERTY_STATE outData; + uint8_t appMsg[MAX_APDU]; + int inLen; + int outLen; + + inData.tag = BOOLEAN_VALUE; + inData.state.booleanValue = true; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.booleanValue == outData.state.booleanValue ); + + /**************** + ***************** + ****************/ + inData.tag = BINARY_VALUE; + inData.state.binaryValue = BINARY_ACTIVE; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.binaryValue == outData.state.binaryValue ); + + /**************** + ***************** + ****************/ + inData.tag = EVENT_TYPE; + inData.state.eventType = EVENT_BUFFER_READY; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.eventType == outData.state.eventType ); + + /**************** + ***************** + ****************/ + inData.tag = POLARITY; + inData.state.polarity = POLARITY_REVERSE; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.polarity == outData.state.polarity ); + + /**************** + ***************** + ****************/ + inData.tag = PROGRAM_CHANGE; + inData.state.programChange = PROGRAM_REQUEST_RESTART; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.programChange == outData.state.programChange ); + + /**************** + ***************** + ****************/ + inData.tag = UNSIGNED_VALUE; + inData.state.unsignedValue = 0xdeadbeef; + + inLen = bacapp_encode_property_state(appMsg, &inData); + + memset(&outData, 0, sizeof(outData)); + + outLen = bacapp_decode_property_state(appMsg, &outData); + + ct_test(pTest, outLen == inLen); + ct_test(pTest, inData.tag == outData.tag ); + ct_test(pTest, inData.state.unsignedValue == outData.state.unsignedValue ); + +} + +#ifdef TEST_PROP_STATES + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Event", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testPropStates); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif // TEST_PROP_STATES +#endif // TEST diff --git a/bacnet-stack/src/bacreal.c b/bacnet-stack/src/bacreal.c index e56f949b..f6abbc9a 100644 --- a/bacnet-stack/src/bacreal.c +++ b/bacnet-stack/src/bacreal.c @@ -76,6 +76,25 @@ int decode_real( return 4; } +int decode_context_real( + uint8_t * apdu, + uint8_t tag_number, + float *real_value) +{ + uint32_t len_value; + int len = 0; + + if (decode_is_context_tag(&apdu[len], tag_number)) { + len += decode_tag_number_and_value(&apdu[len], &tag_number, + &len_value); + len += decode_real(&apdu[len], real_value); + } + else + { + len = -1; + } + return len; +} /* from clause 20.2.6 Encoding of a Real Number Value */ /* returns the number of apdu bytes consumed */ int encode_bacnet_real( diff --git a/bacnet-stack/src/bacstr.c b/bacnet-stack/src/bacstr.c index 4f8fe388..a556b085 100644 --- a/bacnet-stack/src/bacstr.c +++ b/bacnet-stack/src/bacstr.c @@ -96,10 +96,10 @@ uint8_t bitstring_bits_used( } /* returns the number of bytes that a bit string is using */ -int bitstring_bytes_used( +uint8_t bitstring_bytes_used( BACNET_BIT_STRING * bit_string) { - int len = 0; /* return value */ + uint8_t len = 0; /* return value */ uint8_t used_bytes = 0; uint8_t last_bit = 0; diff --git a/bacnet-stack/src/datetime.c b/bacnet-stack/src/datetime.c index 133244d9..84d55d52 100644 --- a/bacnet-stack/src/datetime.c +++ b/bacnet-stack/src/datetime.c @@ -38,6 +38,7 @@ #include #include #include "datetime.h" +#include "bacdcode.h" /* BACnet Date */ /* year = years since 1900 */ @@ -52,6 +53,7 @@ time or date may be interpreted as "any" or "don't care" */ + static bool is_leap_year( uint16_t year) { @@ -73,7 +75,7 @@ static uint8_t month_days( if ((month == 2) && is_leap_year(year)) return 29; else if (month >= 1 && month <= 12) - return month_days[month]; + return (uint8_t)month_days[month]; else return 0; } @@ -128,7 +130,7 @@ static void days_since_epoch_into_ymd( month++; } - day += ((uint8_t) days); + day = (uint8_t) (day + days); if (pYear) *pYear = year; @@ -148,7 +150,7 @@ static uint8_t day_of_week( uint8_t month, uint8_t day) { - return ((uint8_t) (days_since_epoch(year, month, day) % 7) + 1); + return (uint8_t) ((days_since_epoch(year, month, day) % 7) + 1); } /* if the date1 is the same as date2, return is 0 @@ -428,6 +430,93 @@ void datetime_wildcard_set( } } +int bacapp_encode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value) +{ + int len = 0; + int apdu_len = 0; + + + if ( apdu && value ) + { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + len = encode_application_date(&apdu[apdu_len], &value->date); + apdu_len += len; + + len = encode_application_time(&apdu[apdu_len], &value->time); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } + return apdu_len; +} + +int bacapp_decode_datetime( + uint8_t * apdu, + BACNET_DATE_TIME * value) +{ + int len = 0; + int section_len; + + if ( -1 == ( section_len = decode_application_date(&apdu[len], &value->date) ) ) + { + return -1; + } + len += section_len; + + if ( -1 == ( section_len = decode_application_time(&apdu[len], &value->time) ) ) + { + return -1; + } + + len += section_len; + + return len; +} + +int bacapp_decode_context_datetime( + uint8_t * apdu, + uint8_t tag_number, + BACNET_DATE_TIME * value) +{ + int apdu_len = 0; + int len; + + if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) { + apdu_len++; + } + else + { + return -1; + } + + if ( -1 == (len = bacapp_decode_datetime(&apdu[apdu_len], value))) + { + return -1; + } + else + { + apdu_len += len; + } + + if (decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) + { + apdu_len++; + } + else + { + return -1; + } + return apdu_len; +} + + + #ifdef TEST #include @@ -744,6 +833,43 @@ void testBACnetDayOfWeek( ct_test(pTest, dow == 3); } +void testDatetimeCodec( + Test * pTest) +{ + uint8_t apdu[MAX_APDU]; + BACNET_DATE_TIME datetimeIn; + BACNET_DATE_TIME datetimeOut; + int inLen; + int outLen; + + datetimeIn.date.day = 1; + datetimeIn.date.month = 2; + datetimeIn.date.wday = 3; + datetimeIn.date.year = 1904; + + datetimeIn.time.hour = 5; + datetimeIn.time.min = 6; + datetimeIn.time.sec = 7; + datetimeIn.time.hundredths = 8; + + inLen = bacapp_encode_context_datetime(apdu, 10, &datetimeIn); + outLen = bacapp_decode_context_datetime(apdu, 10, &datetimeOut); + + ct_test(pTest, inLen == outLen ); + + ct_test(pTest, datetimeIn.date.day == datetimeOut.date.day); + ct_test(pTest, datetimeIn.date.month == datetimeOut.date.month); + ct_test(pTest, datetimeIn.date.wday == datetimeOut.date.wday); + ct_test(pTest, datetimeIn.date.year == datetimeOut.date.year); + + ct_test(pTest, datetimeIn.time.hour == datetimeOut.time.hour); + ct_test(pTest, datetimeIn.time.min == datetimeOut.time.min); + ct_test(pTest, datetimeIn.time.sec == datetimeOut.time.sec); + ct_test(pTest, datetimeIn.time.hundredths == datetimeOut.time.hundredths); + +} + + #ifdef TEST_DATE_TIME int main( void) @@ -769,6 +895,8 @@ int main( assert(rc); rc = ct_addTestFunction(pTest, testBACnetDateTimeWildcard); assert(rc); + rc = ct_addTestFunction(pTest, testDatetimeCodec); + assert(rc); ct_setStream(pTest, stdout); ct_run(pTest); @@ -777,5 +905,8 @@ int main( return 0; } + + #endif /* TEST_DATE_TIME */ #endif /* TEST */ + diff --git a/bacnet-stack/src/event.c b/bacnet-stack/src/event.c new file mode 100644 index 00000000..c175cdd3 --- /dev/null +++ b/bacnet-stack/src/event.c @@ -0,0 +1,1387 @@ +/*####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 "event.h" +#include "bacdcode.h" +#include "npdu.h" +#include "device.h" +#include "datalink.h" +#include "timestamp.h" + +int uevent_notify_encode_apdu( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_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_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 2; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if ( len > 0 ) + { + apdu_len += len; + } + else + { + apdu_len = 0; + } + } + + return apdu_len; +} + +int cevent_notify_encode_apdu( + uint8_t * apdu, + uint8_t invoke_id, + BACNET_EVENT_NOTIFICATION_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_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 4; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if ( len > 0 ) + { + apdu_len += len; + } + else + { + apdu_len = 0; + } + } + + return apdu_len; +} + +int event_notify_encode_service_request( + uint8_t * apdu, + BACNET_EVENT_NOTIFICATION_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 - processIdentifier */ + len = + encode_context_unsigned(&apdu[apdu_len], 0, + data->processIdentifier); + apdu_len += len; + /* tag 1 - initiatingObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 1, + data->initiatingObjectIdentifier.type, + data->initiatingObjectIdentifier.instance); + apdu_len += len; + + /* tag 2 - eventObjectIdentifier */ + len = + encode_context_object_id(&apdu[apdu_len], 2, + data->eventObjectIdentifier.type, + data->eventObjectIdentifier.instance); + apdu_len += len; + + /* tag 3 - timeStamp */ + + len = + bacapp_encode_context_timestamp(&apdu[apdu_len], 3, + &data->timeStamp); + apdu_len += len; + + /* tag 4 - noticicationClass */ + + len = + encode_context_unsigned(&apdu[apdu_len], 4, + data->notificationClass); + apdu_len += len; + + /* tag 5 - priority */ + + len = + encode_context_unsigned(&apdu[apdu_len], 5, + data->priority); + apdu_len += len; + + /* tag 6 - eventType */ + len = + encode_context_enumerated(&apdu[apdu_len], 6, + data->eventType); + apdu_len += len; + + /* tag 7 - messageText */ + if ( data->messageText ) + { + len = + encode_context_character_string(&apdu[apdu_len], 7, + data->messageText); + apdu_len += len; + } + /* tag 8 - notifyType */ + len = + encode_context_enumerated(&apdu[apdu_len], 8, + data->notifyType); + apdu_len += len; + + switch(data->notifyType) + { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + + len = + encode_context_boolean(&apdu[apdu_len], 9, + data->ackRequired); + apdu_len += len; + + /* tag 10 - fromState */ + len = + encode_context_enumerated(&apdu[apdu_len], 10, + data->fromState); + apdu_len += len; + break; + + default: + break; + } + + /* tag 11 - toState */ + len = + encode_context_enumerated(&apdu[apdu_len], 11, + data->toState); + apdu_len += len; + + switch(data->notifyType) + { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 12 - event values */ + len = encode_opening_tag(&apdu[apdu_len], 12); + apdu_len += len; + + switch(data->eventType) + { + case EVENT_CHANGE_OF_BITSTRING: + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 0, + &data->notificationParams.changeOfBitstring.referencedBitString); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfBitstring.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_STATE: + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = bacapp_encode_property_state(&apdu[apdu_len], + &data->notificationParams.changeOfState.newState); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfState.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_VALUE: + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + switch(data->notificationParams.changeOfValue.tag) + { + case CHANGE_OF_VALUE_REAL: + len = encode_context_real(&apdu[apdu_len], 1, + data->notificationParams.changeOfValue.newValue.changeValue); + apdu_len += len; + break; + + case CHANGE_OF_VALUE_BITS: + len = encode_context_bitstring(&apdu[apdu_len], 0, + &data->notificationParams.changeOfValue.newValue.changedBits); + apdu_len += len; + break; + + default: + return 0; + } + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfValue.statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + break; + + + case EVENT_FLOATING_LIMIT: + len = encode_opening_tag(&apdu[apdu_len], 4); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 0, + data->notificationParams.floatingLimit.referenceValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.floatingLimit.statusFlags); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 2, + data->notificationParams.floatingLimit.setPointValue); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.floatingLimit.errorLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 4); + apdu_len += len; + break; + + + case EVENT_OUT_OF_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 5); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 0, + data->notificationParams.outOfRange.exceedingValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.outOfRange.statusFlags); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 2, + data->notificationParams.outOfRange.deadband); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.outOfRange.exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 5); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_LIFE_SAFETY: + len = encode_opening_tag(&apdu[apdu_len], 8); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 0, + data->notificationParams.changeOfLifeSafety.newState); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 1, + data->notificationParams.changeOfLifeSafety.newMode); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 2, + &data->notificationParams.changeOfLifeSafety.statusFlags); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 3, + data->notificationParams.changeOfLifeSafety.operationExpected); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 8); + apdu_len += len; + break; + + case EVENT_BUFFER_READY: + len = encode_opening_tag(&apdu[apdu_len], 10); + apdu_len += len; + + len = bacapp_encode_context_device_obj_property_ref(&apdu[apdu_len], 0, &data->notificationParams.bufferReady.bufferProperty); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 1, + data->notificationParams.bufferReady.previousNotification); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams.bufferReady.currentNotification); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 10); + apdu_len += len; + break; + + case EVENT_UNSIGNED_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 11); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 0, + data->notificationParams.unsignedRange.exceedingValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.unsignedRange.statusFlags); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams.unsignedRange.exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 11); + apdu_len += len; + break; + + + case EVENT_EXTENDED: + case EVENT_COMMAND_FAILURE: + default: + assert(0); + break; + } + len = encode_closing_tag(&apdu[apdu_len], 12); + apdu_len += len; + break; + } + } + return apdu_len; +} + +int event_notify_decode_service_request( + uint8_t * apdu, + unsigned apdu_len, + BACNET_EVENT_NOTIFICATION_DATA * data) +{ + int len = 0; /* return value */ + int section_length; + uint32_t tmpUInt; + if (apdu_len && data) { + /* tag 0 - processIdentifier */ + if ((section_length = decode_context_unsigned(&apdu[len], 0, + &data->processIdentifier)) == -1) + { + return -1; + } + else + { + len += section_length; + } + + /* tag 1 - initiatingObjectIdentifier */ + if ((section_length = decode_context_object_id(&apdu[len], 1, + &data->initiatingObjectIdentifier.type, + &data->initiatingObjectIdentifier.instance)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 2 - eventObjectIdentifier */ + if ((section_length = decode_context_object_id(&apdu[len], 2, + &data->eventObjectIdentifier.type, + &data->eventObjectIdentifier.instance)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 3 - timeStamp */ + if ((section_length = bacapp_decode_context_timestamp(&apdu[len], 3, + &data->timeStamp)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 4 - noticicationClass */ + if ((section_length = decode_context_unsigned(&apdu[len], 4, + &data->notificationClass)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 5 - priority */ + if ((section_length = decode_context_unsigned(&apdu[len], 5, + &tmpUInt)) == -1) + { + return -1; + } + else + { + if ( tmpUInt > 0xff ) + { + return -1; + } + else + { + data->priority = (uint8_t)tmpUInt; + len += section_length; + } + } + /* tag 6 - eventType */ + if ((section_length = decode_context_enumerated(&apdu[len], 6, + &data->eventType)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 7 - messageText */ + + if ( decode_is_context_tag(&apdu[len], 7) ) + { + if ( data->messageText != NULL ) + { + if ((section_length = decode_context_character_string(&apdu[len], 7, + data->messageText)) == -1) + { + /*FIXME This is an optional parameter */ + return -1; + } + else + { + len += section_length; + } + } + else + { + return -1; + } + } + else + { + if ( data->messageText != NULL ) + { + characterstring_init_ansi(data->messageText, ""); + } + } + + /* tag 8 - notifyType */ + if ((section_length = decode_context_enumerated(&apdu[len], 8, + &data->notifyType)) == -1) + { + return -1; + } + else + { + len += section_length; + } + switch(data->notifyType) + { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + section_length = decode_context_boolean2(&apdu[len], 9, &data->ackRequired); + if ( section_length == -1 ) + { + return -1; + } + len += section_length; + + /* tag 10 - fromState */ + if ((section_length = decode_context_enumerated(&apdu[len], 10, + &data->fromState)) == -1) + { + return -1; + } + else + { + len += section_length; + } + break; + + } + /* tag 11 - toState */ + if ((section_length = decode_context_enumerated(&apdu[len], 11, + &data->toState)) == -1) + { + return -1; + } + else + { + len += section_length; + } + /* tag 12 - eventValues */ + switch(data->notifyType) + { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + if ( decode_is_opening_tag_number(&apdu[len], 12 ) ) + { + len++; + } + else + { + return -1; + } + if ( decode_is_opening_tag_number(&apdu[len], (uint8_t)data->eventType ) ) + { + len++; + } + else + { + return -1; + } + + switch(data->eventType) + { + case EVENT_CHANGE_OF_BITSTRING: + if ( -1 == (section_length = decode_context_bitstring(&apdu[len], 0, &data->notificationParams.changeOfBitstring.referencedBitString))) + { + return -1; + } + len += section_length; + + if ( -1 == (section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.changeOfBitstring.statusFlags))) + { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_STATE: + if ( -1 == (section_length = bacapp_decode_context_property_state(&apdu[len], 0, &data->notificationParams.changeOfState.newState))) + { + return -1; + } + len += section_length; + + if ( -1 == (section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.changeOfState.statusFlags))) + { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_VALUE: + if ( !decode_is_opening_tag_number(&apdu[len], 0)) + { + return -1; + } + len++; + + if ( decode_is_context_tag(&apdu[len], CHANGE_OF_VALUE_BITS)) + { + + if ( -1 == (section_length = decode_context_bitstring(&apdu[len], 0, &data->notificationParams.changeOfValue.newValue.changedBits))) + { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; + } + else if ( decode_is_context_tag(&apdu[len], CHANGE_OF_VALUE_REAL)) + { + if ( -1 == (section_length = decode_context_real(&apdu[len], 1, &data->notificationParams.changeOfValue.newValue.changeValue))) + { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; + } + else + { + return -1; + } + if ( !decode_is_closing_tag_number(&apdu[len], 0)) + { + return -1; + } + len++; + + + if ( -1 == (section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.changeOfValue.statusFlags))) + { + return -1; + } + len += section_length; + break; + + case EVENT_FLOATING_LIMIT: + if ( -1 == ( section_length = decode_context_real(&apdu[len], 0, &data->notificationParams.floatingLimit.referenceValue))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.floatingLimit.statusFlags))) + { + return -1; + } + len += section_length; + if ( -1 == ( section_length = decode_context_real(&apdu[len], 2, &data->notificationParams.floatingLimit.setPointValue))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_real(&apdu[len], 3, &data->notificationParams.floatingLimit.errorLimit))) + { + return -1; + } + len += section_length; + break; + + case EVENT_OUT_OF_RANGE: + if ( -1 == ( section_length = decode_context_real(&apdu[len], 0, &data->notificationParams.outOfRange.exceedingValue))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.outOfRange.statusFlags))) + { + return -1; + } + len += section_length; + if ( -1 == ( section_length = decode_context_real(&apdu[len], 2, &data->notificationParams.outOfRange.deadband))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_real(&apdu[len], 3, &data->notificationParams.outOfRange.exceededLimit))) + { + return -1; + } + len += section_length; + break; + + + case EVENT_CHANGE_OF_LIFE_SAFETY: + if ( -1 == ( section_length = decode_context_enumerated(&apdu[len], 0, &data->notificationParams.changeOfLifeSafety.newState))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_enumerated(&apdu[len], 1, &data->notificationParams.changeOfLifeSafety.newMode))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_bitstring(&apdu[len], 2, &data->notificationParams.changeOfLifeSafety.statusFlags))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_enumerated(&apdu[len], 3, &data->notificationParams.changeOfLifeSafety.operationExpected))) + { + return -1; + } + len += section_length; + break; + + case EVENT_BUFFER_READY: + if ( -1 == ( section_length = bacapp_decode_context_device_obj_property_ref(&apdu[len], 0, &data->notificationParams.bufferReady.bufferProperty))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_unsigned(&apdu[len], 1, &data->notificationParams.bufferReady.previousNotification))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_unsigned(&apdu[len], 2, &data->notificationParams.bufferReady.currentNotification))) + { + return -1; + } + len += section_length; + break; + + case EVENT_UNSIGNED_RANGE: + if ( -1 == ( section_length = decode_context_unsigned(&apdu[len], 0, &data->notificationParams.unsignedRange.exceedingValue))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_bitstring(&apdu[len], 1, &data->notificationParams.unsignedRange.statusFlags))) + { + return -1; + } + len += section_length; + + if ( -1 == ( section_length = decode_context_unsigned(&apdu[len], 2, &data->notificationParams.unsignedRange.exceededLimit))) + { + return -1; + } + len += section_length; + break; + + + default: + return -1; + } + if ( decode_is_closing_tag_number(&apdu[len], (uint8_t)data->eventType ) ) + { + len++; + } + else + { + return -1; + } + if ( decode_is_closing_tag_number(&apdu[len], 12 ) ) + { + len++; + } + else + { + return -1; + } + } + } + + return len; +} + +#ifndef TEST + +int uevent_notify_send( + uint8_t * buffer, + BACNET_EVENT_NOTIFICATION_DATA * data, + BACNET_ADDRESS *dest) +{ + int len = 0; + int pdu_len = 0; + int bytes_sent = 0; + BACNET_NPDU_DATA npdu_data; + + /* encode the NPDU portion of the packet */ + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = npdu_encode_pdu(buffer, dest, NULL, &npdu_data); + /* encode the APDU portion of the packet */ + len = uevent_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; +} +#endif + + +#ifdef TEST + +#include +#include +#include "ctest.h" + + +BACNET_EVENT_NOTIFICATION_DATA data; +BACNET_EVENT_NOTIFICATION_DATA data2; + +void testBitstring( + Test * pTest, + BACNET_BIT_STRING *bs1, + BACNET_BIT_STRING *bs2) +{ + + int i; + ct_test(pTest, bs1->bits_used == bs2->bits_used); + for ( i = 0; i < MAX_BITSTRING_BYTES; i++) + { + ct_test(pTest, bs1->value[i] == bs2->value[i]); + } +} + +void testBaseEventState( + Test * pTest) +{ + ct_test(pTest, data.processIdentifier == data2.processIdentifier); + ct_test(pTest, data.initiatingObjectIdentifier.instance == data2.initiatingObjectIdentifier.instance); + ct_test(pTest, data.initiatingObjectIdentifier.type == data2.initiatingObjectIdentifier.type); + ct_test(pTest, data.eventObjectIdentifier.instance == data2.eventObjectIdentifier.instance); + ct_test(pTest, data.eventObjectIdentifier.type == data2.eventObjectIdentifier.type); + ct_test(pTest, data.notificationClass == data2.notificationClass); + ct_test(pTest, data.priority == data2.priority); + ct_test(pTest, data.notifyType == data2.notifyType); + ct_test(pTest, data.fromState == data2.fromState); + ct_test(pTest, data.toState == data2.toState); + ct_test(pTest, data.toState == data2.toState); + + if ( data.messageText != NULL && data2.messageText != NULL ) + { + ct_test(pTest, data.messageText->encoding == data2.messageText->encoding); + ct_test(pTest, data.messageText->length == data2.messageText->length); + ct_test(pTest, strcmp(data.messageText->value, data2.messageText->value) == 0); + } + + ct_test(pTest, data.timeStamp.tag == data2.timeStamp.tag); + + switch(data.timeStamp.tag) + { + case TIME_STAMP_SEQUENCE: + ct_test(pTest, data.timeStamp.value.sequenceNum == data2.timeStamp.value.sequenceNum); + break; + + case TIME_STAMP_DATETIME: + ct_test(pTest, data.timeStamp.value.dateTime.time.hour == data2.timeStamp.value.dateTime.time.hour); + ct_test(pTest, data.timeStamp.value.dateTime.time.min == data2.timeStamp.value.dateTime.time.min); + ct_test(pTest, data.timeStamp.value.dateTime.time.sec == data2.timeStamp.value.dateTime.time.sec); + ct_test(pTest, data.timeStamp.value.dateTime.time.hundredths == data2.timeStamp.value.dateTime.time.hundredths); + + ct_test(pTest, data.timeStamp.value.dateTime.date.day == data2.timeStamp.value.dateTime.date.day); + ct_test(pTest, data.timeStamp.value.dateTime.date.month == data2.timeStamp.value.dateTime.date.month); + ct_test(pTest, data.timeStamp.value.dateTime.date.wday == data2.timeStamp.value.dateTime.date.wday); + ct_test(pTest, data.timeStamp.value.dateTime.date.year == data2.timeStamp.value.dateTime.date.year); + break; + + case TIME_STAMP_TIME: + ct_test(pTest, data.timeStamp.value.time.hour == data2.timeStamp.value.time.hour); + ct_test(pTest, data.timeStamp.value.time.min == data2.timeStamp.value.time.min); + ct_test(pTest, data.timeStamp.value.time.sec == data2.timeStamp.value.time.sec); + ct_test(pTest, data.timeStamp.value.time.hundredths == data2.timeStamp.value.time.hundredths); + break; + + default: + ct_fail(pTest, "Unknown type"); + break; + } +} + +void testEventEventState( + Test * pTest) +{ + uint8_t buffer[1000]; + int inLen; + int outLen; + BACNET_CHARACTER_STRING messageText; + BACNET_CHARACTER_STRING messageText2; + characterstring_init_ansi(&messageText, "This is a test of the message text\n"); + + data.messageText = &messageText; + data2.messageText = &messageText2; + + data.processIdentifier = 1234; + data.initiatingObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.initiatingObjectIdentifier.instance = 100; + data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.eventObjectIdentifier.instance = 200; + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + data.notificationClass = 50; + data.priority = 50; + data.notifyType = NOTIFY_ALARM; + data.fromState = EVENT_STATE_NORMAL; + data.toState = EVENT_STATE_OFFNORMAL; + + data.eventType = EVENT_CHANGE_OF_STATE; + data.notificationParams.changeOfState.newState.tag = UNITS; + data.notificationParams.changeOfState.newState.state.units = UNITS_SQUARE_METERS; + + bitstring_init(&data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + + inLen = event_notify_encode_service_request(&buffer[0], &data); + + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.changeOfState.newState.tag == data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, data.notificationParams.changeOfState.newState.state.units == data2.notificationParams.changeOfState.newState.state.units); + + testBitstring(pTest, + &data.notificationParams.changeOfState.statusFlags, + &data2.notificationParams.changeOfState.statusFlags); + + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Same, but timestamp of + */ + data.timeStamp.tag = TIME_STAMP_DATETIME; + data.timeStamp.value.dateTime.time.hour = 1; + data.timeStamp.value.dateTime.time.min = 2; + data.timeStamp.value.dateTime.time.sec = 3; + data.timeStamp.value.dateTime.time.hundredths = 4; + + data.timeStamp.value.dateTime.date.day = 1; + data.timeStamp.value.dateTime.date.month = 1; + data.timeStamp.value.dateTime.date.wday = 1; + data.timeStamp.value.dateTime.date.year = 1945; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + ct_test(pTest, data.notificationParams.changeOfState.newState.tag == data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, data.notificationParams.changeOfState.newState.state.units == data2.notificationParams.changeOfState.newState.state.units); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Event Type = EVENT_CHANGE_OF_BITSTRING + */ + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + + data.eventType = EVENT_CHANGE_OF_BITSTRING; + + bitstring_init(&data.notificationParams.changeOfBitstring.referencedBitString); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.referencedBitString, 0, true); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.referencedBitString, 1, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.referencedBitString, 2, true); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.referencedBitString, 2, false); + + bitstring_init(&data.notificationParams.changeOfBitstring.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + testBitstring(pTest, + &data.notificationParams.changeOfBitstring.referencedBitString, + &data2.notificationParams.changeOfBitstring.referencedBitString); + + testBitstring(pTest, + &data.notificationParams.changeOfBitstring.statusFlags, + &data2.notificationParams.changeOfBitstring.statusFlags); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - float value + */ + + data.eventType = EVENT_CHANGE_OF_VALUE; + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; + data.notificationParams.changeOfValue.newValue.changeValue = 1.23f; + + bitstring_init(&data.notificationParams.changeOfValue.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + testBitstring(pTest, + &data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags); + + ct_test(pTest, data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + ct_test(pTest, data.notificationParams.changeOfValue.newValue.changeValue == + data2.notificationParams.changeOfValue.newValue.changeValue); + + + + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - bitstring value + */ + + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; + + bitstring_init(&data.notificationParams.changeOfValue.newValue.changedBits); + bitstring_set_bit(&data.notificationParams.changeOfValue.newValue.changedBits, 0, true); + bitstring_set_bit(&data.notificationParams.changeOfValue.newValue.changedBits, 1, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.newValue.changedBits, 2, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.newValue.changedBits, 3, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + testBitstring(pTest, + &data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags); + + ct_test(pTest, data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + testBitstring(pTest, &data.notificationParams.changeOfValue.newValue.changedBits, + &data2.notificationParams.changeOfValue.newValue.changedBits); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_FLOATING_LIMIT + */ + data.eventType = EVENT_FLOATING_LIMIT; + data.notificationParams.floatingLimit.referenceValue = 1.23f; + data.notificationParams.floatingLimit.setPointValue = 2.34f; + data.notificationParams.floatingLimit.errorLimit = 3.45f; + + bitstring_init(&data.notificationParams.floatingLimit.statusFlags); + + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.floatingLimit.referenceValue == + data2.notificationParams.floatingLimit.referenceValue); + + ct_test(pTest, data.notificationParams.floatingLimit.setPointValue == + data2.notificationParams.floatingLimit.setPointValue); + + ct_test(pTest, data.notificationParams.floatingLimit.errorLimit == + data2.notificationParams.floatingLimit.errorLimit); + testBitstring(pTest, + &data.notificationParams.floatingLimit.statusFlags, + &data2.notificationParams.floatingLimit.statusFlags); + + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_OUT_OF_RANGE + */ + data.eventType = EVENT_OUT_OF_RANGE; + data.notificationParams.outOfRange.exceedingValue = 3.45f; + data.notificationParams.outOfRange.deadband = 2.34f; + data.notificationParams.outOfRange.exceededLimit = 1.23f; + + bitstring_init(&data.notificationParams.outOfRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.outOfRange.deadband == + data2.notificationParams.outOfRange.deadband); + + ct_test(pTest, data.notificationParams.outOfRange.exceededLimit == + data2.notificationParams.outOfRange.exceededLimit); + + ct_test(pTest, data.notificationParams.outOfRange.exceedingValue == + data2.notificationParams.outOfRange.exceedingValue); + testBitstring(pTest, + &data.notificationParams.outOfRange.statusFlags, + &data2.notificationParams.outOfRange.statusFlags); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_LIFE_SAFETY + */ + data.eventType = EVENT_CHANGE_OF_LIFE_SAFETY; + data.notificationParams.changeOfLifeSafety.newState = LIFE_SAFETY_STATE_ALARM; + data.notificationParams.changeOfLifeSafety.newMode = LIFE_SAFETY_MODE_ARMED; + data.notificationParams.changeOfLifeSafety.operationExpected = LIFE_SAFETY_OP_RESET; + + bitstring_init(&data.notificationParams.changeOfLifeSafety.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.changeOfLifeSafety.newMode == + data2.notificationParams.changeOfLifeSafety.newMode); + + ct_test(pTest, data.notificationParams.changeOfLifeSafety.newState == + data2.notificationParams.changeOfLifeSafety.newState); + + ct_test(pTest, data.notificationParams.changeOfLifeSafety.operationExpected == + data2.notificationParams.changeOfLifeSafety.operationExpected); + + testBitstring(pTest, + &data.notificationParams.changeOfLifeSafety.statusFlags, + &data2.notificationParams.changeOfLifeSafety.statusFlags); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_UNSIGNED_RANGE + */ + data.eventType = EVENT_UNSIGNED_RANGE; + data.notificationParams.unsignedRange.exceedingValue = 1234; + data.notificationParams.unsignedRange.exceededLimit = 2345; + + bitstring_init(&data.notificationParams.unsignedRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.unsignedRange.exceedingValue == + data2.notificationParams.unsignedRange.exceedingValue); + + ct_test(pTest, data.notificationParams.unsignedRange.exceededLimit == + data2.notificationParams.unsignedRange.exceededLimit); + + testBitstring(pTest, + &data.notificationParams.unsignedRange.statusFlags, + &data2.notificationParams.unsignedRange.statusFlags); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_BUFFER_READY + */ + data.eventType = EVENT_BUFFER_READY; + data.notificationParams.bufferReady.previousNotification = 1234; + data.notificationParams.bufferReady.currentNotification = 2345; + data.notificationParams.bufferReady.bufferProperty.deviceIndentifier.type = OBJECT_DEVICE; + data.notificationParams.bufferReady.bufferProperty.deviceIndentifier.instance = 500; + data.notificationParams.bufferReady.bufferProperty.objectIdentifier.type = OBJECT_ANALOG_INPUT; + data.notificationParams.bufferReady.bufferProperty.objectIdentifier.instance = 100; + data.notificationParams.bufferReady.bufferProperty.propertyIdentifier = PROP_PRESENT_VALUE; + data.notificationParams.bufferReady.bufferProperty.arrayIndex = 0; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, data.notificationParams.bufferReady.previousNotification == + data2.notificationParams.bufferReady.previousNotification); + + ct_test(pTest, data.notificationParams.bufferReady.currentNotification == + data2.notificationParams.bufferReady.currentNotification); + + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.deviceIndentifier.type == + data2.notificationParams.bufferReady.bufferProperty.deviceIndentifier.type); + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.deviceIndentifier.instance == + data2.notificationParams.bufferReady.bufferProperty.deviceIndentifier.instance); + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.objectIdentifier.instance == + data2.notificationParams.bufferReady.bufferProperty.objectIdentifier.instance); + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.objectIdentifier.type == + data2.notificationParams.bufferReady.bufferProperty.objectIdentifier.type); + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.propertyIdentifier == + data2.notificationParams.bufferReady.bufferProperty.propertyIdentifier); + + ct_test(pTest, data.notificationParams.bufferReady.bufferProperty.arrayIndex == + data2.notificationParams.bufferReady.bufferProperty.arrayIndex); +} + +#ifdef TEST_EVENT + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Event", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testEventEventState); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif // TEST_EVENT +#endif // TEST diff --git a/bacnet-stack/src/timestamp.c b/bacnet-stack/src/timestamp.c new file mode 100644 index 00000000..b2495fb7 --- /dev/null +++ b/bacnet-stack/src/timestamp.c @@ -0,0 +1,249 @@ +#include "assert.h" +#include "timestamp.h" + +int bacapp_encode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; + + if (value && apdu) { + len = encode_opening_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + + switch(value->tag) + { + case TIME_STAMP_TIME: + len = encode_context_time(&apdu[apdu_len], 0, &value->value.time); + break; + + case TIME_STAMP_SEQUENCE: + len = encode_context_unsigned(&apdu[apdu_len], 1, value->value.sequenceNum); + break; + + case TIME_STAMP_DATETIME: + len = bacapp_encode_context_datetime(&apdu[apdu_len], 2, &value->value.dateTime); + break; + + default: + len = 0; + assert(0); + break; + } + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], tag_number); + apdu_len += len; + } + return apdu_len; +} + +int bacapp_decode_context_timestamp( + uint8_t * apdu, + uint8_t tag_number, + BACNET_TIMESTAMP * value) +{ + int len = 0; + int section_len; + uint32_t len_value_type; + uint32_t sequenceNum; + + + if (decode_is_opening_tag_number(&apdu[len], tag_number)) { + len++; + + section_len = + decode_tag_number_and_value(&apdu[len], &value->tag, + &len_value_type); + + if ( -1 == section_len ) + { + return -1; + } + switch( value->tag ) + { + case TIME_STAMP_TIME: + if ( (section_len = decode_context_bacnet_time(&apdu[len], TIME_STAMP_TIME, &value->value.time)) == -1 ) + { + return -1; + } + else + { + len += section_len; + } + break; + + case TIME_STAMP_SEQUENCE: + if ( (section_len = decode_context_unsigned(&apdu[len], TIME_STAMP_SEQUENCE, &sequenceNum)) == -1 ) + { + return -1; + } + else + { + if ( sequenceNum <= 0xffff ) + { + len += section_len; + value->value.sequenceNum = (uint16_t)sequenceNum; + } + else + { + return -1; + } + } + break; + + case TIME_STAMP_DATETIME: + if ( (section_len = bacapp_decode_context_datetime(&apdu[len], TIME_STAMP_DATETIME, &value->value.dateTime)) == -1 ) + { + return -1; + } + else + { + len += section_len; + } + break; + + default: + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], tag_number)) { + len++; + } + else + { + return -1; + } + } + return len; +} + +#ifdef TEST + +#include +#include +#include "ctest.h" + + +void testTimestampSequence( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_SEQUENCE; + testTimestampIn.value.sequenceNum = 0x1234; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, testTimestampIn.value.sequenceNum == testTimestampOut.value.sequenceNum); +} + +void testTimestampTime( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_TIME; + testTimestampIn.value.time.hour = 1; + testTimestampIn.value.time.min = 2; + testTimestampIn.value.time.sec = 3; + testTimestampIn.value.time.hundredths = 4; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, testTimestampIn.value.time.hour == testTimestampOut.value.time.hour); + ct_test(pTest, testTimestampIn.value.time.min == testTimestampOut.value.time.min); + ct_test(pTest, testTimestampIn.value.time.sec == testTimestampOut.value.time.sec); + ct_test(pTest, testTimestampIn.value.time.hundredths == testTimestampOut.value.time.hundredths); +} + +void testTimestampTimeDate( + Test * pTest) +{ + BACNET_TIMESTAMP testTimestampIn; + BACNET_TIMESTAMP testTimestampOut; + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + + testTimestampIn.tag = TIME_STAMP_DATETIME; + testTimestampIn.value.dateTime.time.hour = 1; + testTimestampIn.value.dateTime.time.min = 2; + testTimestampIn.value.dateTime.time.sec = 3; + testTimestampIn.value.dateTime.time.hundredths = 4; + + testTimestampIn.value.dateTime.date.year = 1901; + testTimestampIn.value.dateTime.date.month = 1; + testTimestampIn.value.dateTime.date.wday = 2; + testTimestampIn.value.dateTime.date.day = 3; + + memset(&testTimestampOut, 0, sizeof(testTimestampOut)); + + inLen = bacapp_encode_context_timestamp(buffer, 2, &testTimestampIn); + outLen = bacapp_decode_context_timestamp(buffer, 2, &testTimestampOut); + + ct_test(pTest, inLen == outLen); + ct_test(pTest, testTimestampIn.tag == testTimestampOut.tag); + ct_test(pTest, testTimestampIn.value.dateTime.time.hour == testTimestampOut.value.dateTime.time.hour); + ct_test(pTest, testTimestampIn.value.dateTime.time.min == testTimestampOut.value.dateTime.time.min); + ct_test(pTest, testTimestampIn.value.dateTime.time.sec == testTimestampOut.value.dateTime.time.sec); + ct_test(pTest, testTimestampIn.value.dateTime.time.hundredths == testTimestampOut.value.dateTime.time.hundredths); + + ct_test(pTest, testTimestampIn.value.dateTime.date.year == testTimestampOut.value.dateTime.date.year); + ct_test(pTest, testTimestampIn.value.dateTime.date.month == testTimestampOut.value.dateTime.date.month); + ct_test(pTest, testTimestampIn.value.dateTime.date.wday == testTimestampOut.value.dateTime.date.wday); + ct_test(pTest, testTimestampIn.value.dateTime.date.day == testTimestampOut.value.dateTime.date.day); + +} + +#ifdef TEST_TIME_STAMP + +int main( + void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Time Stamp", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testTimestampSequence); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimestampTime); + assert(rc); + + rc = ct_addTestFunction(pTest, testTimestampTimeDate); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void) ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif // TEST_TIME_STAMP +#endif // TEST +