/************************************************************************** * * 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. * * Additional changes, Copyright (c) 2018 Ed Hague * * 2018.06.17 - Attempting to write to Object_Name returned *UNKNOWN_PROPERTY. Now returns WRITE_ACCESS_DENIED * *********************************************************************/ #include #include #include #include #include "bacnet/basic/binding/address.h" #include "bacnet/bacdef.h" #include "bacnet/bacdcode.h" #include "bacnet/bacenum.h" #include "bacnet/bacapp.h" #include "bacnet/basic/services.h" #include "bacnet/config.h" #include "bacnet/basic/object/device.h" #include "bacnet/event.h" #include "bacnet/basic/services.h" #include "bacnet/basic/tsm/tsm.h" #include "bacnet/wp.h" #include "bacnet/basic/object/nc.h" #include "bacnet/datalink/datalink.h" #if PRINT_ENABLED #include #define PRINTF(...) fprintf(stderr, __VA_ARGS__) #else #define PRINTF(...) #endif #ifndef MAX_NOTIFICATION_CLASSES #define MAX_NOTIFICATION_CLASSES 2 #endif #if defined(INTRINSIC_REPORTING) 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; unsigned i; 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[TRANSITION_TO_OFFNORMAL] = 255; /* The lowest priority for Normal message. */ NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] = 255; /* The lowest priority for Normal message. */ NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] = 255; /* PRINTF lowest priority for Normal message. */ /* configure for every day, all day long */ for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { NC_Info[NotifyIdx].Recipient_List->ValidDays |= (1 << i); } NC_Info[NotifyIdx].Recipient_List->FromTime.hour = 0; NC_Info[NotifyIdx].Recipient_List->FromTime.min = 0; NC_Info[NotifyIdx].Recipient_List->FromTime.sec = 0; NC_Info[NotifyIdx].Recipient_List->FromTime.hundredths = 0; NC_Info[NotifyIdx].Recipient_List->ToTime.hour = 23; NC_Info[NotifyIdx].Recipient_List->ToTime.min = 59; NC_Info[NotifyIdx].Recipient_List->ToTime.sec = 59; NC_Info[NotifyIdx].Recipient_List->ToTime.hundredths = 0; NC_Info[NotifyIdx].Recipient_List->Transitions = TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_FAULT_MASKED | TRANSITION_TO_NORMAL_MASKED; NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false; NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false; NC_Info[NotifyIdx].Recipient_List->Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; NC_Info[NotifyIdx].Recipient_List->Recipient._.DeviceIdentifier = 4194303; } 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) { apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL]); apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentNotify->Priority[TRANSITION_TO_FAULT]); apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentNotify->Priority[TRANSITION_TO_NORMAL]); } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { 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; uint8_t TmpPriority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */ bool status = false; int iOffset; uint8_t idx; int len = 0; CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index( wp_data->object_instance)]; /* decode some of the request */ len = bacapp_decode_application_data( wp_data->application_data, wp_data->application_data_len, &value); if (len < 0) { /* error while decoding - a value larger than we can handle */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } if ((wp_data->object_property != PROP_PRIORITY) && (wp_data->array_index != BACNET_ARRAY_ALL)) { /* only array properties can have array options */ wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; return false; } switch (wp_data->object_property) { case PROP_PRIORITY: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); if (status) { if (wp_data->array_index == 0) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; status = false; } else if (wp_data->array_index == BACNET_ARRAY_ALL) { iOffset = 0; for (idx = 0; idx < MAX_BACNET_EVENT_TRANSITION; idx++) { 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; status = false; break; } if (value.type.Unsigned_Int > 255) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; status = false; break; } TmpPriority[idx] = (uint8_t)value.type.Unsigned_Int; iOffset += len; } if (status == true) { for (idx = 0; idx < MAX_BACNET_EVENT_TRANSITION; idx++) CurrentNotify->Priority[idx] = TmpPriority[idx]; } } else if (wp_data->array_index <= 3) { if (value.type.Unsigned_Int > 255) { wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; status = false; } else 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; status = false; } } break; case PROP_ACK_REQUIRED: status = write_property_type_valid( wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); 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)); idx = 0; iOffset = 0; /* 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++) { BACNET_ADDRESS src = { 0 }; unsigned max_apdu = 0; int32_t DeviceID; CurrentNotify->Recipient_List[idx] = TmpNotify.Recipient_List[idx]; if (CurrentNotify->Recipient_List[idx] .Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { /* copy Device_ID */ DeviceID = CurrentNotify->Recipient_List[idx] .Recipient._.DeviceIdentifier; address_bind_request(DeviceID, &max_apdu, &src); } else if (CurrentNotify->Recipient_List[idx] .Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { /* copy Address */ /* src = * CurrentNotify->Recipient_List[idx].Recipient._.Address; */ /* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu, * &src); */ } } status = true; case PROP_OBJECT_NAME: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } return status; } void Notification_Class_Get_Priorities( uint32_t Object_Instance, uint32_t *pPriorityArray) { NOTIFICATION_CLASS_INFO *CurrentNotify; uint32_t object_index; int i; object_index = Notification_Class_Instance_To_Index(Object_Instance); if (object_index < MAX_NOTIFICATION_CLASSES) CurrentNotify = &NC_Info[object_index]; else { for (i = 0; i < 3; i++) pPriorityArray[i] = 255; return; /* unknown object */ } for (i = 0; i < 3; i++) pPriorityArray[i] = CurrentNotify->Priority[i]; } static bool IsRecipientActive( BACNET_DESTINATION *pBacDest, uint8_t EventToState) { BACNET_DATE_TIME DateTime; /* valid Transitions */ switch (EventToState) { case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: case EVENT_STATE_LOW_LIMIT: if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED)) return false; break; case EVENT_STATE_FAULT: if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED)) return false; break; case EVENT_STATE_NORMAL: if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED)) return false; break; default: return false; /* shouldn't happen */ } /* get actual date and time */ Device_getCurrentDateTime(&DateTime); /* valid Days */ if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays)) return false; /* valid FromTime */ if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0) return false; /* valid ToTime */ if (datetime_compare_time(&pBacDest->ToTime, &DateTime.time) < 0) return false; return true; } void Notification_Class_common_reporting_function( BACNET_EVENT_NOTIFICATION_DATA *event_data) { /* Fill the parameters common for all types of events. */ NOTIFICATION_CLASS_INFO *CurrentNotify; BACNET_DESTINATION *pBacDest; uint32_t notify_index; uint8_t index; notify_index = Notification_Class_Instance_To_Index(event_data->notificationClass); if (notify_index < MAX_NOTIFICATION_CLASSES) CurrentNotify = &NC_Info[notify_index]; else return; /* Initiating Device Identifier */ event_data->initiatingObjectIdentifier.type = OBJECT_DEVICE; event_data->initiatingObjectIdentifier.instance = Device_Object_Instance_Number(); /* Priority and AckRequired */ switch (event_data->toState) { case EVENT_STATE_NORMAL: event_data->priority = CurrentNotify->Priority[TRANSITION_TO_NORMAL]; event_data->ackRequired = (CurrentNotify->Ack_Required & TRANSITION_TO_NORMAL_MASKED) ? true : false; break; case EVENT_STATE_FAULT: event_data->priority = CurrentNotify->Priority[TRANSITION_TO_FAULT]; event_data->ackRequired = (CurrentNotify->Ack_Required & TRANSITION_TO_FAULT_MASKED) ? true : false; break; case EVENT_STATE_OFFNORMAL: case EVENT_STATE_HIGH_LIMIT: case EVENT_STATE_LOW_LIMIT: event_data->priority = CurrentNotify->Priority[TRANSITION_TO_OFFNORMAL]; event_data->ackRequired = (CurrentNotify->Ack_Required & TRANSITION_TO_OFFNORMAL_MASKED) ? true : false; break; default: /* shouldn't happen */ break; } /* send notifications for active recipients */ PRINTF("Notification Class[%u]: send notifications\n", event_data->notificationClass); /* pointer to first recipient */ pBacDest = &CurrentNotify->Recipient_List[0]; for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) { /* check if recipient is defined */ if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED) break; /* recipient doesn't defined - end of list */ if (IsRecipientActive(pBacDest, event_data->toState)) { BACNET_ADDRESS dest; uint32_t device_id; unsigned max_apdu; /* Process Identifier */ event_data->processIdentifier = pBacDest->ProcessIdentifier; /* send notification */ if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { /* send notification to the specified device */ device_id = pBacDest->Recipient._.DeviceIdentifier; PRINTF("Notification Class[%u]: send notification to %u\n", event_data->notificationClass, (unsigned)device_id); if (pBacDest->ConfirmedNotify == true) Send_CEvent_Notify(device_id, event_data); else if (address_get_by_device(device_id, &max_apdu, &dest)) Send_UEvent_Notify( Handler_Transmit_Buffer, event_data, &dest); } else if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { PRINTF("Notification Class[%u]: send notification to ADDR\n", event_data->notificationClass); /* send notification to the address indicated */ if (pBacDest->ConfirmedNotify == true) { if (address_get_device_id(&dest, &device_id)) Send_CEvent_Notify(device_id, event_data); } else { dest = pBacDest->Recipient._.Address; Send_UEvent_Notify( Handler_Transmit_Buffer, event_data, &dest); } } } } } /* This function tries to find the addresses of the defined devices. */ /* It should be called periodically (example once per minute). */ void Notification_Class_find_recipient(void) { NOTIFICATION_CLASS_INFO *CurrentNotify; BACNET_DESTINATION *pBacDest; BACNET_ADDRESS src = { 0 }; unsigned max_apdu = 0; uint32_t notify_index; uint32_t DeviceID; uint8_t idx; for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES; notify_index++) { /* pointer to current notification */ CurrentNotify = &NC_Info[Notification_Class_Instance_To_Index(notify_index)]; /* pointer to first recipient */ pBacDest = &CurrentNotify->Recipient_List[0]; for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) { if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) { /* Device ID */ DeviceID = CurrentNotify->Recipient_List[idx] .Recipient._.DeviceIdentifier; /* Send who_ is request only when address of device is unknown. */ if (!address_bind_request(DeviceID, &max_apdu, &src)) Send_WhoIs(DeviceID, DeviceID); } else if (CurrentNotify->Recipient_List[idx] .Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { } } } } #endif /* defined(INTRINSIC_REPORTING) */