diff --git a/bacnet-stack/demo/object/nc.c b/bacnet-stack/demo/object/nc.c new file mode 100644 index 00000000..af1eb532 --- /dev/null +++ b/bacnet-stack/demo/object/nc.c @@ -0,0 +1,691 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include + +#include "bacdef.h" +#include "bacdcode.h" +#include "bacenum.h" +#include "bacapp.h" +#include "config.h" +#include "device.h" +#include "handlers.h" +#include "wp.h" +#include "nc.h" + +#include "datalink.h" //for send who is +#include "whois.h" + + +#ifndef MAX_NOTIFICATION_CLASSES +#define MAX_NOTIFICATION_CLASSES 2 +#endif + + +static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Notification_Properties_Required[] = +{ + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_NOTIFICATION_CLASS, + PROP_PRIORITY, + PROP_ACK_REQUIRED, + PROP_RECIPIENT_LIST, + -1 +}; + +static const int Notification_Properties_Optional[] = +{ + PROP_DESCRIPTION, + -1 +}; + +static const int Notification_Properties_Proprietary[] = +{ + -1 +}; + +void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary) +{ + if (pRequired) + *pRequired = Notification_Properties_Required; + if (pOptional) + *pOptional = Notification_Properties_Optional; + if (pProprietary) + *pProprietary = Notification_Properties_Proprietary; + return; +} + +void Notification_Class_Init(void) +{ + uint8_t NotifyIdx = 0; + + for(NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) + { + /* init with zeros */ + memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + /* set the basic parameters*/ + NC_Info[NotifyIdx].Ack_Required = 0; + NC_Info[NotifyIdx].Priority[0] = 255; // The lowest priority for Normal message. + NC_Info[NotifyIdx].Priority[1] = 255; // The lowest priority for Normal message. + NC_Info[NotifyIdx].Priority[2] = 255; // The lowest priority for Normal message. + } + + /* init with special values for tests */ +// NC_Info[0].Ack_Required = TRANSITION_TO_NORMAL_MASKED; +// NC_Info[0].Priority[0] = 248; +// NC_Info[0].Priority[1] = 192; +// NC_Info[0].Priority[2] = 200; +// NC_Info[0].Recipient_List[0].ValidDays = 0x1F; /* from monday to friday */ +// NC_Info[0].Recipient_List[0].FromTime.hour = 6; +// NC_Info[0].Recipient_List[0].FromTime.min = 15; +// NC_Info[0].Recipient_List[0].FromTime.sec = 10; +// NC_Info[0].Recipient_List[0].FromTime.hundredths = 0; +// NC_Info[0].Recipient_List[0].ToTime.hour = 23; +// NC_Info[0].Recipient_List[0].ToTime.min = 0; +// NC_Info[0].Recipient_List[0].ToTime.sec = 59; +// NC_Info[0].Recipient_List[0].ToTime.hundredths = 0; +// NC_Info[0].Recipient_List[0].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[0] = 0xC0; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[1] = 0xA8; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[2] = 0x01; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[3] = 0x65; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[4] = 0xBA; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac[5] = 0xC0; +// NC_Info[0].Recipient_List[0].Recipient._.Address.mac_len = 6; +// NC_Info[0].Recipient_List[0].ConfirmedNotify = true; +// NC_Info[0].Recipient_List[0].Transitions = +// TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_NORMAL_MASKED; + + + return; +} + + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Notification_Class_Valid_Instance( + uint32_t object_instance) +{ + unsigned int index; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) + return true; + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Notification_Class_Count(void) +{ + return MAX_NOTIFICATION_CLASSES; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Notification_Class_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance) +{ + unsigned index = MAX_NOTIFICATION_CLASSES; + + if (object_instance < MAX_NOTIFICATION_CLASSES) + index = object_instance; + + return index; +} + +bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Notification_Class_Instance_To_Index(object_instance); + if (index < MAX_NOTIFICATION_CLASSES) { + sprintf(text_string, "NOTIFICATION CLASS %lu", (unsigned long) index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + + + +int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + BACNET_CHARACTER_STRING char_string; + BACNET_OCTET_STRING octet_string; + BACNET_BIT_STRING bit_string; + uint8_t *apdu = NULL; + uint8_t u8Val; + int idx; + int apdu_len = 0; /* return value */ + + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + apdu = rpdata->application_data; + CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)]; + + switch (rpdata->object_property) + { + case PROP_OBJECT_IDENTIFIER: + apdu_len = + encode_application_object_id(&apdu[0], OBJECT_NOTIFICATION_CLASS, + rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Notification_Class_Object_Name(rpdata->object_instance, &char_string); + apdu_len = + encode_application_character_string(&apdu[0], &char_string); + break; + + case PROP_OBJECT_TYPE: + apdu_len = + encode_application_enumerated(&apdu[0], OBJECT_NOTIFICATION_CLASS); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len += encode_application_unsigned(&apdu[0], rpdata->object_instance); + break; + + case PROP_PRIORITY: + if (rpdata->array_index == 0) + apdu_len += encode_application_unsigned(&apdu[0], 3); + else + { + if(rpdata->array_index == BACNET_ARRAY_ALL) + { + /* TO-OFFNORMAL */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[0]); + /* TO-FAULT */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[1]); + /* TO-NORMAL */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[2]); + } + else if(rpdata->array_index <= 3) + { + apdu_len += encode_application_unsigned(&apdu[apdu_len], + CurrentNotify->Priority[rpdata->array_index - 1]); + } + else + { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = -1; + } + } + break; + + case PROP_ACK_REQUIRED: + u8Val = CurrentNotify->Ack_Required; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); + /* encode bitstring */ + apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); + break; + + case PROP_RECIPIENT_LIST: + /* encode all entry of Recipient_List */ + for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + { + BACNET_DESTINATION *RecipientEntry; + int i = 0; + + /* get pointer of current element for Recipient_List - easier for use */ + RecipientEntry = &CurrentNotify->Recipient_List[idx]; + if(RecipientEntry->Recipient.RecipientType != RECIPIENT_TYPE_NOTINITIALIZED) + { + /* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday */ + u8Val = 0x01; + bitstring_init(&bit_string); + + for(i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) + { + if(RecipientEntry->ValidDays & u8Val) + bitstring_set_bit(&bit_string, i, true); + else + bitstring_set_bit(&bit_string, i, false); + u8Val <<= 1; /* next day */ + } + apdu_len += encode_application_bitstring(&apdu[apdu_len],&bit_string); + + /* From Time */ + apdu_len += encode_application_time(&apdu[apdu_len], + &RecipientEntry->FromTime); + + /* To Time */ + apdu_len += encode_application_time(&apdu[apdu_len], + &RecipientEntry->ToTime); + + /* + BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress + } */ + + /* CHOICE - device [0] BACnetObjectIdentifier */ + if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) + { + apdu_len += encode_context_object_id(&apdu[apdu_len], 0, OBJECT_DEVICE, + RecipientEntry->Recipient._.DeviceIdentifier); + } + /* CHOICE - address [1] BACnetAddress */ + else if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) + { + /* opening tag 1 */ + apdu_len += encode_opening_tag(&apdu[apdu_len], 1); + /* network-number Unsigned16, */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->Recipient._.Address.net); + + /* mac-address OCTET STRING */ + if (RecipientEntry->Recipient._.Address.net) + { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.adr, + RecipientEntry->Recipient._.Address.len); + } + else + { + octetstring_init(&octet_string, + RecipientEntry->Recipient._.Address.mac, + RecipientEntry->Recipient._.Address.mac_len); + } + apdu_len += encode_application_octet_string(&apdu[apdu_len], &octet_string); + + /* closing tag 1 */ + apdu_len += encode_closing_tag(&apdu[apdu_len], 1); + + } + else {;} /* shouldn't happen */ + + /* Process Identifier - Unsigned32 */ + apdu_len += encode_application_unsigned(&apdu[apdu_len], + RecipientEntry->ProcessIdentifier); + + /* Issue Confirmed Notifications - boolean */ + apdu_len += encode_application_boolean(&apdu[apdu_len], + RecipientEntry->ConfirmedNotify); + + /* Transitions - BACnet Event Transition Bits [bitstring] */ + u8Val = RecipientEntry->Transitions; + + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (u8Val & TRANSITION_TO_FAULT_MASKED ) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (u8Val & TRANSITION_TO_NORMAL_MASKED ) ? true : false ); + + apdu_len += encode_application_bitstring(&apdu[apdu_len], &bit_string); + } + } + break; + + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = -1; + break; + } + + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY) && + (rpdata->array_index != BACNET_ARRAY_ALL)) + { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + + + +bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data) +{ + NOTIFICATION_CLASS_INFO *CurrentNotify; + NOTIFICATION_CLASS_INFO TmpNotify; + BACNET_APPLICATION_DATA_VALUE value; + bool status = false; + int iOffset = 0; + uint8_t idx = 0; + int len = 0; + + + + CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(wp_data->object_instance)]; + + // decode the some of the request + len = bacapp_decode_application_data(wp_data->application_data, + wp_data->application_data_len, &value); + + switch (wp_data->object_property) + { + case PROP_PRIORITY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if(status) { + if (wp_data->array_index == 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + else if(wp_data->array_index == BACNET_ARRAY_ALL) { + /// FIXME: wite all array + } + else if(wp_data->array_index <= 3) { + CurrentNotify->Priority[wp_data->array_index - 1] = + value.type.Unsigned_Int; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + } + break; + + case PROP_ACK_REQUIRED: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_BIT_STRING, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if(value.type.Bit_String.bits_used == 3) { + CurrentNotify->Ack_Required = + value.type.Bit_String.value[0]; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + + case PROP_RECIPIENT_LIST: + + memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO)); + + /* decode all packed */ + while (iOffset < wp_data->application_data_len) + { + /* Decode Valid Days */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK) + /* store value */ + TmpNotify.Recipient_List[idx].ValidDays = value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + + iOffset += len; + /* Decode From Time */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].FromTime = value.type.Time; + + iOffset += len; + /* Decode To Time */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ToTime = value.type.Time; + + iOffset += len; + /* context tag [0] - Device */ + if(decode_is_context_tag(&wp_data->application_data[iOffset], 0)) + { + TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; + /* Decode Network Number */ + len = bacapp_decode_context_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value, + PROP_RECIPIENT_LIST); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier = + value.type.Object_Id.instance; + + iOffset += len; + } + /* opening tag [1] - Recipient */ + else if(decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) + { + iOffset++; + TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; + /* Decode Network Number */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].Recipient._.Address.net = value.type.Unsigned_Int; + + iOffset += len; + /* Decode Address */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + if (TmpNotify.Recipient_List[idx].Recipient._.Address.net == 0) { + memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.mac, + value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address.mac_len = + value.type.Octet_String.length; + } + else { + memcpy(TmpNotify.Recipient_List[idx].Recipient._.Address.adr, + value.type.Octet_String.value, + value.type.Octet_String.length); + TmpNotify.Recipient_List[idx].Recipient._.Address.len = + value.type.Octet_String.length; + } + + iOffset += len; + /* closing tag [1] - Recipient */ + if(decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) + iOffset++; + else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + } + else { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + /* Process Identifier */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ProcessIdentifier = value.type.Unsigned_Int; + + iOffset += len; + /* Issue Confirmed Notifications */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + /* store value */ + TmpNotify.Recipient_List[idx].ConfirmedNotify = value.type.Boolean; + + iOffset += len; + /* Transitions */ + len = bacapp_decode_application_data( + &wp_data->application_data[iOffset], + wp_data->application_data_len, &value); + + if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) { + /* Bad decode, wrong tag or following required parameter missing */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + return false; + } + + if (value.type.Bit_String.bits_used == MAX_BACNET_EVENT_TRANSITION) + /* store value */ + TmpNotify.Recipient_List[idx].Transitions = value.type.Bit_String.value[0]; + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_OTHER; + return false; + } + iOffset += len; + + /* Increasing element of list */ + if(++idx >= NC_MAX_RECIPIENTS) { + wp_data->error_class = ERROR_CLASS_RESOURCES; + wp_data->error_code = ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY; + return false; + } + } + + /* Decoded all recipient list */ + /* copy elements from temporary object */ + for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; + + status = true; + + break; + + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} diff --git a/bacnet-stack/demo/object/nc.h b/bacnet-stack/demo/object/nc.h new file mode 100644 index 00000000..c785b9de --- /dev/null +++ b/bacnet-stack/demo/object/nc.h @@ -0,0 +1,112 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#ifndef NC_H +#define NC_H + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define NC_MAX_RECIPIENTS 10 + +#define RECIPIENT_TYPE_NOTINITIALIZED 0 +#define RECIPIENT_TYPE_DEVICE 1 +#define RECIPIENT_TYPE_ADDRESS 2 + + +/* BACnetRecipient sructuer*/ +/* +BACnetRecipient ::= CHOICE { + device [0] BACnetObjectIdentifier, + address [1] BACnetAddress +} +*/ +typedef struct BACnet_Recipient { + uint8_t RecipientType; /* Type of Recipient */ + union { + uint32_t DeviceIdentifier; + BACNET_ADDRESS Address; + } _; +} BACNET_RECIPIENT; + + + +/* BACnetDestination structure */ +typedef struct BACnet_Destination { + uint8_t ValidDays; + BACNET_TIME FromTime; + BACNET_TIME ToTime; + BACNET_RECIPIENT Recipient; + uint32_t ProcessIdentifier; + uint8_t Transitions; + bool ConfirmedNotify; +} BACNET_DESTINATION; + +/* Structure containing configuration for a Notification Class */ +typedef struct Notification_Class_info { + uint8_t Priority[3]; /* BACnetARRAY[3] of Unsigned */ + uint8_t Ack_Required; /* BACnetEventTransitionBits */ + BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */ +} NOTIFICATION_CLASS_INFO; + + + void Notification_Class_Property_Lists( + const int **pRequired, + const int **pOptional, + const int **pProprietary); + + void Notification_Class_Init(void); + + bool Notification_Class_Valid_Instance( + uint32_t object_instance); + unsigned Notification_Class_Count(void); + uint32_t Notification_Class_Index_To_Instance(unsigned index); + unsigned Notification_Class_Instance_To_Index( + uint32_t object_instance); + bool Notification_Class_Object_Name( + uint32_t object_instance, + BACNET_CHARACTER_STRING *object_name); + + int Notification_Class_Read_Property( + BACNET_READ_PROPERTY_DATA * rpdata); + + bool Notification_Class_Write_Property( + BACNET_WRITE_PROPERTY_DATA * wp_data); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#define NOTIFICATION_CLASS_OBJ_FUNCTIONS \ + OBJECT_NOTIFICATION_CLASS, Notification_Class_Init, Notification_Class_Count, \ + Notification_Class_Index_To_Instance, Notification_Class_Valid_Instance, \ + Notification_Class_Object_Name, Notification_Class_Read_Property, \ + Notification_Class_Write_Property, Notification_Class_Property_Lists, \ + NULL, NULL, NULL + +#endif // NC_H diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c index e8eb666e..ce78d898 100644 --- a/bacnet-stack/demo/server/main.c +++ b/bacnet-stack/demo/server/main.c @@ -60,6 +60,7 @@ #include "lsp.h" #include "mso.h" #include "ms-input.h" +#include "nc.h" #include "trendlog.h" #if defined(BACFILE) #include "bacfile.h" @@ -74,6 +75,9 @@ static object_functions_t Object_Table[] = { {BINARY_INPUT_OBJ_FUNCTIONS}, {BINARY_OUTPUT_OBJ_FUNCTIONS}, {BINARY_VALUE_OBJ_FUNCTIONS}, +#if defined(INTRINSIC_REPORTING) + {NOTIFICATION_CLASS_OBJ_FUNCTIONS}, +#endif {LIFE_SAFETY_POINT_OBJ_FUNCTIONS}, {LOAD_CONTROL_OBJ_FUNCTIONS}, {MULTI_STATE_OUTPUT_OBJ_FUNCTIONS}, diff --git a/bacnet-stack/include/bacdef.h b/bacnet-stack/include/bacdef.h index 8e40fb06..f3ff5588 100644 --- a/bacnet-stack/include/bacdef.h +++ b/bacnet-stack/include/bacdef.h @@ -49,6 +49,9 @@ #define BACNET_PROTOCOL_VERSION 1 #define BACNET_PROTOCOL_REVISION 10 +/* Intrinsic reporting funcionality */ +#define INTRINSIC_REPORTING + /* largest BACnet Instance Number */ /* Also used as a device instance number wildcard address */ #define BACNET_MAX_INSTANCE (0x3FFFFF) diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index 329e3a80..1b9d3f7b 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -1464,6 +1464,13 @@ typedef enum BACnetEventTransitionBits { MAX_BACNET_EVENT_TRANSITION = 3 } BACNET_EVENT_TRANSITION_BITS; +/* Not in standard, but useful for store Ack_Required */ +typedef enum BACnetEventTransitionMask { + TRANSITION_TO_OFFNORMAL_MASKED = 1, + TRANSITION_TO_FAULT_MASKED = 2, + TRANSITION_TO_NORMAL_MASKED = 4 +} BACNET_EVENT_TRANSITION_MASK; + /* The Network Reject Reasons for NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK */ typedef enum { NETWORK_REJECT_UNKNOWN_ERROR = 0, diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index 048f35eb..3c2f3a13 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -118,6 +118,7 @@ OBJECT_SRC = \ $(BACNET_OBJECT)/lsp.c \ $(BACNET_OBJECT)/ms-input.c \ $(BACNET_OBJECT)/mso.c \ + $(BACNET_OBJECT)/nc.c \ $(BACNET_OBJECT)/trendlog.c \ $(BACNET_OBJECT)/bacfile.c diff --git a/bacnet-stack/src/bacapp.c b/bacnet-stack/src/bacapp.c index 0afb661b..c569996e 100644 --- a/bacnet-stack/src/bacapp.c +++ b/bacnet-stack/src/bacapp.c @@ -637,6 +637,17 @@ BACNET_APPLICATION_TAG bacapp_context_tag_type( } break; + case PROP_RECIPIENT_LIST: + /* List of BACnetDestination */ + switch (tag_number) { + case 0: /* Device Object ID */ + tag = BACNET_APPLICATION_TAG_OBJECT_ID; + break; + default: + break; + } + break; + default: break; }