From 19a77ba46e71bbe9450c8d5f71b5d1c9264a2359 Mon Sep 17 00:00:00 2001 From: k001a Date: Tue, 5 Jul 2011 19:44:36 +0000 Subject: [PATCH] Created descriptor for analog-value objects, - writable Relinquish_Default and Units - added properties required for intrinsic reporting. Intrinsic reporting for analog-value. First commit. Not everything finished (inter alia Time_Delay, Event_Time_Stamps, ACK_Required doesn't work now). Initialize all objects in Device_Init function. Added function to read current time. Defined enum BACNET_LIMIT_ENABLE. Started cache address. --- bacnet-stack/demo/object/av.c | 515 +++++++++++++++++++++++++++--- bacnet-stack/demo/object/av.h | 28 ++ bacnet-stack/demo/object/device.c | 135 +++++++- bacnet-stack/demo/object/device.h | 14 + bacnet-stack/demo/object/nc.c | 253 +++++++++++++-- bacnet-stack/demo/object/nc.h | 20 +- bacnet-stack/demo/server/main.c | 43 ++- bacnet-stack/include/bacenum.h | 12 + 8 files changed, 919 insertions(+), 101 deletions(-) diff --git a/bacnet-stack/demo/object/av.c b/bacnet-stack/demo/object/av.c index cff42a65..97615ba8 100644 --- a/bacnet-stack/demo/object/av.c +++ b/bacnet-stack/demo/object/av.c @@ -1,6 +1,7 @@ /************************************************************************** * * Copyright (C) 2006 Steve Karg +* 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 @@ -28,13 +29,17 @@ #include #include #include +#include #include "bacdef.h" #include "bacdcode.h" #include "bacenum.h" #include "bacapp.h" #include "config.h" /* the custom stuff */ #include "wp.h" +#include "rp.h" +#include "nc.h" #include "av.h" +#include "device.h" #include "handlers.h" #ifndef MAX_ANALOG_VALUES @@ -45,16 +50,10 @@ /* a particular value. When the priorities are not in use, they */ /* will be relinquished (i.e. set to the NULL level). */ #define ANALOG_LEVEL_NULL 255 -/* When all the priorities are level null, the present value returns */ -/* the Relinquish Default value */ -#define ANALOG_RELINQUISH_DEFAULT 0 -/* Here is our Priority Array. They are supposed to be Real, but */ -/* we don't have that kind of memory, so we will use a single byte */ -/* and load a Real for returning the value when asked. */ -static uint8_t Analog_Value_Level[MAX_ANALOG_VALUES][BACNET_MAX_PRIORITY]; -/* Writable out-of-service allows others to play with our Present Value */ -/* without changing the physical output */ -static bool Analog_Value_Out_Of_Service[MAX_ANALOG_VALUES]; + + + +ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES]; /* These three arrays are used by the ReadPropertyMultiple handler */ static const int Analog_Value_Properties_Required[] = { @@ -73,6 +72,18 @@ static const int Analog_Value_Properties_Optional[] = { PROP_DESCRIPTION, PROP_PRIORITY_ARRAY, PROP_RELINQUISH_DEFAULT, +#if defined(INTRINSIC_REPORTING) + PROP_TIME_DELAY, + PROP_NOTIFICATION_CLASS, + PROP_HIGH_LIMIT, + PROP_LOW_LIMIT, + PROP_DEADBAND, + PROP_LIMIT_ENABLE, + PROP_EVENT_ENABLE, + PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, + PROP_EVENT_TIME_STAMPS, +#endif -1 }; @@ -100,11 +111,16 @@ void Analog_Value_Init( { unsigned i, j; - /* initialize all the analog output priority arrays to NULL */ for (i = 0; i < MAX_ANALOG_VALUES; i++) { + memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR)); + /* initialize all the analog output priority arrays to NULL */ for (j = 0; j < BACNET_MAX_PRIORITY; j++) { - Analog_Value_Level[i][j] = ANALOG_LEVEL_NULL; + AV_Descr[i].Priority_Array[j] = ANALOG_LEVEL_NULL; } + AV_Descr[i].Units = UNITS_PERCENT; + AV_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + AV_Descr[i].Notification_Class = BACNET_MAX_INSTANCE; } return; @@ -158,15 +174,18 @@ bool Analog_Value_Present_Value_Set( float value, uint8_t priority) { + ANALOG_VALUE_DESCR *CurrentAV; unsigned index = 0; bool status = false; index = Analog_Value_Instance_To_Index(object_instance); if (index < MAX_ANALOG_VALUES) { + CurrentAV = &AV_Descr[index]; if (priority && (priority <= BACNET_MAX_PRIORITY) && (priority != 6 /* reserved */ ) && - (value >= 0.0) && (value <= 100.0)) { - Analog_Value_Level[index][priority - 1] = (uint8_t) value; + (value >= 0.0) && (value <= 100.0)) + { + CurrentAV->Priority_Array[priority - 1] = (uint8_t) value; /* Note: you could set the physical output here to the next highest priority, or to the relinquish default if no priorities are set. @@ -183,15 +202,20 @@ bool Analog_Value_Present_Value_Set( float Analog_Value_Present_Value( uint32_t object_instance) { - float value = ANALOG_RELINQUISH_DEFAULT; + ANALOG_VALUE_DESCR *CurrentAV; + float value = 0; unsigned index = 0; unsigned i = 0; index = Analog_Value_Instance_To_Index(object_instance); if (index < MAX_ANALOG_VALUES) { + CurrentAV = &AV_Descr[index]; + /* When all the priorities are level null, the present value returns */ + /* the Relinquish Default value */ + value = CurrentAV->Relinquish_Default; for (i = 0; i < BACNET_MAX_PRIORITY; i++) { - if (Analog_Value_Level[index][i] != ANALOG_LEVEL_NULL) { - value = Analog_Value_Level[index][i]; + if (CurrentAV->Priority_Array[i] != ANALOG_LEVEL_NULL) { + value = CurrentAV->Priority_Array[i]; break; } } @@ -230,32 +254,45 @@ int Analog_Value_Read_Property( unsigned i = 0; bool state = false; uint8_t *apdu = NULL; + ANALOG_VALUE_DESCR *CurrentAV; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } + apdu = rpdata->application_data; + + object_index = Analog_Value_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return BACNET_STATUS_ERROR; + switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id(&apdu[0], OBJECT_ANALOG_VALUE, rpdata->object_instance); break; + case PROP_OBJECT_NAME: case PROP_DESCRIPTION: Analog_Value_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_ANALOG_VALUE); break; + case PROP_PRESENT_VALUE: real_value = Analog_Value_Present_Value(rpdata->object_instance); apdu_len = encode_application_real(&apdu[0], real_value); break; + case PROP_STATUS_FLAGS: bitstring_init(&bit_string); bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, false); @@ -264,19 +301,26 @@ int Analog_Value_Read_Property( bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, false); apdu_len = encode_application_bitstring(&apdu[0], &bit_string); break; + case PROP_EVENT_STATE: - apdu_len = - encode_application_enumerated(&apdu[0], EVENT_STATE_NORMAL); +#if defined(INTRINSIC_REPORTING) + apdu_len = encode_application_enumerated(&apdu[0], + CurrentAV->Event_State); +#else + apdu_len = encode_application_enumerated(&apdu[0], + EVENT_STATE_NORMAL); +#endif break; + case PROP_OUT_OF_SERVICE: - object_index = - Analog_Value_Instance_To_Index(rpdata->object_instance); - state = Analog_Value_Out_Of_Service[object_index]; + state = CurrentAV->Out_Of_Service; apdu_len = encode_application_boolean(&apdu[0], state); break; + case PROP_UNITS: - apdu_len = encode_application_enumerated(&apdu[0], UNITS_PERCENT); + apdu_len = encode_application_enumerated(&apdu[0], CurrentAV->Units); break; + case PROP_PRIORITY_ARRAY: /* Array element zero is the number of elements in the array */ if (rpdata->array_index == 0) @@ -285,15 +329,12 @@ int Analog_Value_Read_Property( /* if no index was specified, then try to encode the entire list */ /* into one packet. */ else if (rpdata->array_index == BACNET_ARRAY_ALL) { - object_index = - Analog_Value_Instance_To_Index(rpdata->object_instance); for (i = 0; i < BACNET_MAX_PRIORITY; i++) { /* FIXME: check if we have room before adding it to APDU */ - if (Analog_Value_Level[object_index][i] == - ANALOG_LEVEL_NULL) + if (CurrentAV->Priority_Array[i] == ANALOG_LEVEL_NULL) len = encode_application_null(&apdu[apdu_len]); else { - real_value = Analog_Value_Level[object_index][i]; + real_value = CurrentAV->Priority_Array[i]; len = encode_application_real(&apdu[apdu_len], real_value); @@ -309,14 +350,12 @@ int Analog_Value_Read_Property( } } } else { - object_index = - Analog_Value_Instance_To_Index(rpdata->object_instance); if (rpdata->array_index <= BACNET_MAX_PRIORITY) { - if (Analog_Value_Level[object_index][rpdata->array_index - - 1] == ANALOG_LEVEL_NULL) + if (CurrentAV->Priority_Array[rpdata->array_index - 1] + == ANALOG_LEVEL_NULL) apdu_len = encode_application_null(&apdu[0]); else { - real_value = Analog_Value_Level[object_index] + real_value = CurrentAV->Priority_Array [rpdata->array_index - 1]; apdu_len = encode_application_real(&apdu[0], real_value); @@ -328,10 +367,91 @@ int Analog_Value_Read_Property( } } break; + case PROP_RELINQUISH_DEFAULT: - real_value = ANALOG_RELINQUISH_DEFAULT; + real_value = CurrentAV->Relinquish_Default; apdu_len = encode_application_real(&apdu[0], real_value); break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + apdu_len = encode_application_unsigned(&apdu[0], CurrentAV->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = encode_application_unsigned(&apdu[0], CurrentAV->Notification_Class); + break; + + case PROP_HIGH_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAV->High_Limit); + break; + + case PROP_LOW_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAV->Low_Limit); + break; + + case PROP_DEADBAND: + apdu_len = encode_application_real(&apdu[0], CurrentAV->Deadband); + break; + + case PROP_LIMIT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, 0, + (CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE ) ? true : false ); + bitstring_set_bit(&bit_string, 1, + (CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) ? true : false ); + + apdu_len = encode_application_bitstring(&apdu[0],&bit_string); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (CurrentAV->Event_Enable & EVENT_ENABLE_TO_FAULT ) ? true : false ); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL ) ? true : false ); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, true); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, true); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, true); + + /// Fixme: finish it + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = encode_application_enumerated(&apdu[0], + CurrentAV->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + /* Array element zero is the number of elements in the array */ + if (rpdata->array_index == 0) + apdu_len = encode_application_unsigned(&apdu[0], 3); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + /// Fixme: + } + else if (rpdata->array_index <= 3) { + /// Fixme: + } + else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif + default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -359,6 +479,7 @@ bool Analog_Value_Write_Property( uint8_t level = ANALOG_LEVEL_NULL; int len = 0; BACNET_APPLICATION_DATA_VALUE value; + ANALOG_VALUE_DESCR *CurrentAV; /* decode the some of the request */ len = @@ -371,6 +492,13 @@ bool Analog_Value_Write_Property( wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } + + object_index = Analog_Value_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return false; + switch (wp_data->object_property) { case PROP_PRESENT_VALUE: if (value.tag == BACNET_APPLICATION_TAG_REAL) { @@ -396,13 +524,10 @@ bool Analog_Value_Write_Property( &wp_data->error_class, &wp_data->error_code); if (status) { level = ANALOG_LEVEL_NULL; - object_index = - Analog_Value_Instance_To_Index(wp_data-> - object_instance); priority = wp_data->priority; if (priority && (priority <= BACNET_MAX_PRIORITY)) { priority--; - Analog_Value_Level[object_index][priority] = level; + CurrentAV->Priority_Array[priority] = level; /* Note: you could set the physical output here to the next highest priority, or to the relinquish default if no priorities are set. @@ -417,19 +542,140 @@ bool Analog_Value_Write_Property( } } break; + case PROP_OUT_OF_SERVICE: status = WPValidateArgType(&value, BACNET_APPLICATION_TAG_BOOLEAN, &wp_data->error_class, &wp_data->error_code); if (status) { - object_index = - Analog_Value_Instance_To_Index(wp_data->object_instance); - Analog_Value_Out_Of_Service[object_index] = value.type.Boolean; + CurrentAV->Out_Of_Service = value.type.Boolean; } break; + + case PROP_UNITS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Units = value.type.Enumerated; + } + break; + + case PROP_RELINQUISH_DEFAULT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + if (status) { + CurrentAV->Relinquish_Default = value.type.Real; + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Time_Delay = value.type.Unsigned_Int; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_HIGH_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->High_Limit = value.type.Real; + } + break; + + case PROP_LOW_LIMIT: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Low_Limit = value.type.Real; + } + break; + + case PROP_DEADBAND: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_REAL, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + CurrentAV->Deadband = value.type.Real; + } + break; + + case PROP_LIMIT_ENABLE: + 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 == 2) { + CurrentAV->Limit_Enable = value.type.Bit_String.value[0]; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_EVENT_ENABLE: + 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) { + CurrentAV->Event_Enable = value.type.Bit_String.value[0]; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; + + case PROP_NOTIFY_TYPE: + status = + WPValidateArgType(&value, BACNET_APPLICATION_TAG_ENUMERATED, + &wp_data->error_class, &wp_data->error_code); + + if (status) { + if(value.type.Bit_String.bits_used > NOTIFY_EVENT) { + CurrentAV->Event_Enable = value.type.Enumerated; + } + else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + } + } + break; +#endif + default: wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; break; } @@ -437,6 +683,191 @@ bool Analog_Value_Write_Property( } +void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) +{ + BACNET_EVENT_NOTIFICATION_DATA event_data; + BACNET_CHARACTER_STRING msgText; + ANALOG_VALUE_DESCR *CurrentAV; + unsigned int object_index; + uint8_t FromState; + uint8_t ToState; + float ExceededLimit; + float PresentVal; + + + object_index = Analog_Value_Instance_To_Index(object_instance); + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else + return; + + /* check limits */ + if (!CurrentAV->Limit_Enable) + return; /* limits are not configured */ + + /* actual Present_Value */ + PresentVal = Analog_Value_Present_Value(object_instance); + FromState = CurrentAV->Event_State; + switch (CurrentAV->Event_State) + { + case EVENT_STATE_NORMAL: + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the High_Limit for a minimum + period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-OFFNORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAV->High_Limit) && + /// (Deadband >= CurrentAV->Time_Delay) && + ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL)) + { + CurrentAV->Event_State = EVENT_STATE_HIGH_LIMIT; + break; + } + + /* A TO-OFFNORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAV->Low_Limit) && + /// (Deadband >= CurrentAV->Time_Delay) && + ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == EVENT_ENABLE_TO_OFFNORMAL)) + { + CurrentAV->Event_State = EVENT_STATE_LOW_LIMIT; + break; + } + break; + + case EVENT_STATE_HIGH_LIMIT: + /* Once exceeded, the Present_Value must fall below the High_Limit minus + the Deadband before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must fall below the High_Limit minus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the HighLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal < CurrentAV->High_Limit - CurrentAV->Deadband) && + /// (Deadband >= CurrentAV->Time_Delay) && + ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL)) + { + CurrentAV->Event_State = EVENT_STATE_NORMAL; + } + break; + + case EVENT_STATE_LOW_LIMIT: + /* Once the Present_Value has fallen below the Low_Limit, + the Present_Value must exceed the Low_Limit plus the Deadband + before a TO-NORMAL event is generated under these conditions: + (a) the Present_Value must exceed the Low_Limit plus the Deadband + for a minimum period of time, specified in the Time_Delay property, and + (b) the LowLimitEnable flag must be set in the Limit_Enable property, and + (c) the TO-NORMAL flag must be set in the Event_Enable property. */ + if ((PresentVal > CurrentAV->Low_Limit + CurrentAV->Deadband) && + /// (Deadband >= CurrentAV->Time_Delay) && + ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == EVENT_ENABLE_TO_NORMAL)) + { + CurrentAV->Event_State = EVENT_STATE_NORMAL; + } + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = CurrentAV->Event_State; + + if (FromState != ToState) + { + /* Event_State has changed. + Need to fill only the basic parameters of this type of event. + Other parameters will be filled in common function. */ + + switch (ToState) + { + case EVENT_STATE_HIGH_LIMIT: + ExceededLimit = CurrentAV->High_Limit; + characterstring_init_ansi(&msgText, + "Goes to high limit"); + break; + + case EVENT_STATE_LOW_LIMIT: + ExceededLimit = CurrentAV->Low_Limit; + characterstring_init_ansi(&msgText, + "Goes to low limit"); + break; + + case EVENT_STATE_NORMAL: + if(FromState == EVENT_STATE_HIGH_LIMIT) { + ExceededLimit = CurrentAV->High_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from high limit"); + } + else { + ExceededLimit = CurrentAV->Low_Limit; + characterstring_init_ansi(&msgText, + "Back to normal state from low limit"); + } + break; + + default: + ExceededLimit = 0; + break; + } /* switch (ToState) */ + + + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = OBJECT_ANALOG_VALUE; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp*/ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + + /* Notification Class */ + event_data.notificationClass = CurrentAV->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_OUT_OF_RANGE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + event_data.notifyType = CurrentAV->Notify_Type; + + /* From State */ + event_data.fromState = FromState; + + /* To State */ + event_data.toState = CurrentAV->Event_State; + + /* Event Values */ + event_data.notificationParams.outOfRange.exceedingValue = PresentVal; + + bitstring_init(&event_data.notificationParams.outOfRange.statusFlags); + bitstring_set_bit(&event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, CurrentAV->Event_State ? true : false); + bitstring_set_bit(&event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, CurrentAV->Out_Of_Service); + + event_data.notificationParams.outOfRange.deadband = CurrentAV->Deadband; + + event_data.notificationParams.outOfRange.exceededLimit = ExceededLimit; + + /* add data from notification class */ + Notification_Class_common_reporting_function(&event_data); + } +} + + + #ifdef TEST #include #include diff --git a/bacnet-stack/demo/object/av.h b/bacnet-stack/demo/object/av.h index 7de0139b..1ca68a42 100644 --- a/bacnet-stack/demo/object/av.h +++ b/bacnet-stack/demo/object/av.h @@ -1,6 +1,7 @@ /************************************************************************** * * Copyright (C) 2006 Steve Karg +* 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 @@ -35,6 +36,30 @@ #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ + + typedef struct analog_value_descr { + uint8_t Event_State : 3; + bool Out_Of_Service; + uint8_t Units; + /* Here is our Priority Array. They are supposed to be Real, but */ + /* we don't have that kind of memory, so we will use a single byte */ + /* and load a Real for returning the value when asked. */ + uint8_t Priority_Array[BACNET_MAX_PRIORITY]; + float Relinquish_Default; +#if defined(INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + float High_Limit; + float Low_Limit; + float Deadband; + uint8_t Limit_Enable : 2; + uint8_t Event_Enable : 3; + uint8_t Acked_Transitions : 3; + uint8_t Notify_Type : 1; +#endif + } ANALOG_VALUE_DESCR; + + void Analog_Value_Property_Lists( const int **pRequired, const int **pOptional, @@ -65,6 +90,9 @@ extern "C" { float Analog_Value_Present_Value( uint32_t object_instance); + void Analog_Value_Intrinsic_Reporting( + uint32_t object_instance); + void Analog_Value_Init( void); diff --git a/bacnet-stack/demo/object/device.c b/bacnet-stack/demo/object/device.c index e9fcdc9a..d01df5c0 100644 --- a/bacnet-stack/demo/object/device.c +++ b/bacnet-stack/demo/object/device.c @@ -46,6 +46,24 @@ #include "address.h" /* os specfic includes */ #include "timer.h" +/* include the device object */ +#include "device.h" +#include "ai.h" +#include "ao.h" +#include "av.h" +#include "bi.h" +#include "bo.h" +#include "bv.h" +#include "lc.h" +#include "lsp.h" +#include "mso.h" +#include "ms-input.h" +#include "nc.h" +#include "trendlog.h" +#if defined(BACFILE) +#include "bacfile.h" +#endif + #if defined(__BORLANDC__) /* seems to not be defined in time.h as specified by The Open Group */ @@ -63,9 +81,77 @@ extern int Routed_Device_Read_Property_Local( extern bool Routed_Device_Write_Property_Local( BACNET_WRITE_PROPERTY_DATA * wp_data); -/* Defined in user application; - object functions for all included BACnet objects */ -static object_functions_t *Object_Table; + +static object_functions_t Object_Table[] = { + { + OBJECT_DEVICE, NULL, Device_Count, /* don't init - recursive! */ + Device_Index_To_Instance, Device_Valid_Object_Instance_Number, + Device_Object_Name, Device_Read_Property_Local, + Device_Write_Property_Local, Device_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count, + Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance, + Analog_Input_Object_Name, Analog_Input_Read_Property, + NULL, Analog_Input_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count, + Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance, + Analog_Value_Object_Name, Analog_Value_Read_Property, + Analog_Value_Write_Property, Analog_Value_Property_Lists, + NULL, NULL, NULL, Analog_Value_Intrinsic_Reporting}, { + OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count, + Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance, + Binary_Input_Object_Name, Binary_Input_Read_Property, + NULL, Binary_Input_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count, + Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance, + Binary_Value_Object_Name, Binary_Value_Read_Property, + Binary_Value_Write_Property, Binary_Value_Property_Lists, + NULL, NULL, NULL, NULL}, { +#if defined(INTRINSIC_REPORTING) + 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, NULL}, { +#endif + OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count, + Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance, + Life_Safety_Point_Object_Name, Life_Safety_Point_Read_Property, + Life_Safety_Point_Write_Property, Life_Safety_Point_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_LOAD_CONTROL, Load_Control_Init, Load_Control_Count, + Load_Control_Index_To_Instance, Load_Control_Valid_Instance, + Load_Control_Object_Name, Load_Control_Read_Property, + Load_Control_Write_Property, Load_Control_Property_Lists, + NULL, NULL, NULL}, { + OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init, Multistate_Output_Count, + Multistate_Output_Index_To_Instance, Multistate_Output_Valid_Instance, + Multistate_Output_Object_Name, Multistate_Output_Read_Property, + Multistate_Output_Write_Property, Multistate_Output_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_MULTI_STATE_INPUT, Multistate_Input_Init, Multistate_Input_Count, + Multistate_Input_Index_To_Instance, Multistate_Input_Valid_Instance, + Multistate_Input_Object_Name, Multistate_Input_Read_Property, + Multistate_Input_Write_Property, Multistate_Input_Property_Lists, + NULL, NULL, NULL, NULL}, { + OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count, + Trend_Log_Index_To_Instance, Trend_Log_Valid_Instance, + Trend_Log_Object_Name, Trend_Log_Read_Property, + Trend_Log_Write_Property, Trend_Log_Property_Lists, + TrendLogGetRRInfo, NULL, NULL, NULL}, { +#if defined(BACFILE) + OBJECT_FILE, bacfile_init, bacfile_count, + bacfile_index_to_instance, bacfile_valid_instance, + bacfile_object_name, bacfile_read_property, + bacfile_write_property, BACfile_Property_Lists, + NULL, NULL, NULL, NULL}, { +#endif + MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL} +}; + /** Glue function to let the Device object, when called by a handler, * lookup which Object type needs to be invoked. @@ -872,6 +958,14 @@ int tm_isdst Daylight Savings flag. } } +void Device_getCurrentDateTime(BACNET_DATE_TIME * DateTime) +{ + Update_Current_Time(); + + DateTime->date = Local_Date; + DateTime->time = Local_Time; +} + /* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or BACNET_STATUS_ABORT for abort message */ int Device_Read_Property_Local( @@ -1390,6 +1484,36 @@ bool Device_Encode_Value_List( return (status); } + +void Device_local_reporting(uint32_t milliseconds) +{ + struct object_functions *pObject; + uint32_t objects_count; + uint32_t object_instance; + int object_type; + uint32_t idx; + + objects_count = Device_Object_List_Count(); + + /* loop for all objects */ + for (idx = 0; idx < objects_count; idx++) + { + Device_Object_List_Identifier(idx, &object_type, + &object_instance); + + pObject = Device_Objects_Find_Functions(object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(object_instance)) { + if (pObject->Object_Intrinsic_Reporting) { + pObject->Object_Intrinsic_Reporting( + object_instance); + } + } + } + } +} + /** Looks up the requested Object to see if the functionality is supported. * @ingroup ObjHelpers * @param [in] The object type to be looked up. @@ -1425,8 +1549,9 @@ void Device_Init( struct object_functions *pObject = NULL; characterstring_init_ansi(&My_Object_Name, "SimpleServer"); - /* call all the child objects initialization functions */ - Object_Table = object_table; + /* we don't use the object table passed in + since there is extra stuff we don't need in there. */ + (void)object_table; pObject = &Object_Table[0]; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { if (pObject->Object_Init) { diff --git a/bacnet-stack/demo/object/device.h b/bacnet-stack/demo/object/device.h index b911ba77..474b71f5 100644 --- a/bacnet-stack/demo/object/device.h +++ b/bacnet-stack/demo/object/device.h @@ -112,6 +112,15 @@ typedef bool( uint32_t object_instance, BACNET_PROPERTY_VALUE * value_list); +/** Intrinsic Reporting funcionality. + * @ingroup ObjHelpers + * @param [in] Object instance. + */ +typedef void( + *object_intrinsic_reporting_function) ( + uint32_t object_instance); + + /** Defines the group of object helper functions for any supported Object. * @ingroup ObjHelpers * Each Object must provide some implementation of each of these helpers @@ -134,6 +143,7 @@ typedef struct object_functions { rr_info_function Object_RR_Info; object_iterate_function Object_Iterator; object_value_list_function Object_Value_List; + object_intrinsic_reporting_function Object_Intrinsic_Reporting; } object_functions_t; /* String Lengths - excluding any nul terminator */ @@ -203,6 +213,8 @@ extern "C" { rr_info_function Device_Objects_RR_Info( BACNET_OBJECT_TYPE object_type); + void Device_getCurrentDateTime(BACNET_DATE_TIME * DateTime); + void Device_Property_Lists( const int **pRequired, const int **pOptional, @@ -325,6 +337,8 @@ extern "C" { bool Device_Write_Property_Local( BACNET_WRITE_PROPERTY_DATA * wp_data); + void Device_local_reporting(uint32_t milliseconds); + /* Prototypes for Routing functionality in the Device Object. * Enable by defining BAC_ROUTING in config.h and including gw_device.c * in the build (lib/Makefile). diff --git a/bacnet-stack/demo/object/nc.c b/bacnet-stack/demo/object/nc.c index af1eb532..24e3290a 100644 --- a/bacnet-stack/demo/object/nc.c +++ b/bacnet-stack/demo/object/nc.c @@ -25,20 +25,22 @@ #include #include #include +#include +#include "address.h" #include "bacdef.h" #include "bacdcode.h" #include "bacenum.h" #include "bacapp.h" +#include "client.h" #include "config.h" #include "device.h" +#include "event.h" #include "handlers.h" +#include "txbuf.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 @@ -89,15 +91,15 @@ void Notification_Class_Init(void) { uint8_t NotifyIdx = 0; - for(NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) + for (NotifyIdx = 0; NotifyIdx < MAX_NOTIFICATION_CLASSES; NotifyIdx++) { /* init with zeros */ memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO)); - /* set the basic parameters*/ + /* 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. + 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 */ @@ -111,7 +113,7 @@ void Notification_Class_Init(void) // 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.min = 48; // 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; @@ -122,6 +124,7 @@ void Notification_Class_Init(void) // 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].ProcessIdentifier = 112233; // NC_Info[0].Recipient_List[0].ConfirmedNotify = true; // NC_Info[0].Recipient_List[0].Transitions = // TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_NORMAL_MASKED; @@ -244,7 +247,7 @@ int Notification_Class_Read_Property( apdu_len += encode_application_unsigned(&apdu[0], 3); else { - if(rpdata->array_index == BACNET_ARRAY_ALL) + if (rpdata->array_index == BACNET_ARRAY_ALL) { /* TO-OFFNORMAL */ apdu_len += encode_application_unsigned(&apdu[apdu_len], @@ -256,7 +259,7 @@ int Notification_Class_Read_Property( apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentNotify->Priority[2]); } - else if(rpdata->array_index <= 3) + else if (rpdata->array_index <= 3) { apdu_len += encode_application_unsigned(&apdu[apdu_len], CurrentNotify->Priority[rpdata->array_index - 1]); @@ -286,22 +289,22 @@ int Notification_Class_Read_Property( case PROP_RECIPIENT_LIST: /* encode all entry of Recipient_List */ - for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + 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) + 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++) + for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) { - if(RecipientEntry->ValidDays & u8Val) + if (RecipientEntry->ValidDays & u8Val) bitstring_set_bit(&bit_string, i, true); else bitstring_set_bit(&bit_string, i, false); @@ -330,7 +333,7 @@ int Notification_Class_Read_Property( RecipientEntry->Recipient._.DeviceIdentifier); } /* CHOICE - address [1] BACnetAddress */ - else if(RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) + else if (RecipientEntry->Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) { /* opening tag 1 */ apdu_len += encode_opening_tag(&apdu[apdu_len], 1); @@ -403,7 +406,6 @@ int Notification_Class_Read_Property( } - bool Notification_Class_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data) { @@ -430,15 +432,15 @@ bool Notification_Class_Write_Property( WPValidateArgType(&value, BACNET_APPLICATION_TAG_UNSIGNED_INT, &wp_data->error_class, &wp_data->error_code); - if(status) { + 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) { + else if (wp_data->array_index == BACNET_ARRAY_ALL) { /// FIXME: wite all array } - else if(wp_data->array_index <= 3) { + else if (wp_data->array_index <= 3) { CurrentNotify->Priority[wp_data->array_index - 1] = value.type.Unsigned_Int; } @@ -526,7 +528,7 @@ bool Notification_Class_Write_Property( iOffset += len; /* context tag [0] - Device */ - if(decode_is_context_tag(&wp_data->application_data[iOffset], 0)) + if (decode_is_context_tag(&wp_data->application_data[iOffset], 0)) { TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_DEVICE; /* Decode Network Number */ @@ -548,7 +550,7 @@ bool Notification_Class_Write_Property( iOffset += len; } /* opening tag [1] - Recipient */ - else if(decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) + else if (decode_is_opening_tag_number(&wp_data->application_data[iOffset], 1)) { iOffset++; TmpNotify.Recipient_List[idx].Recipient.RecipientType = RECIPIENT_TYPE_ADDRESS; @@ -596,7 +598,7 @@ bool Notification_Class_Write_Property( iOffset += len; /* closing tag [1] - Recipient */ - if(decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) + if (decode_is_closing_tag_number(&wp_data->application_data[iOffset], 1)) iOffset++; else { /* Bad decode, wrong tag or following required parameter missing */ @@ -665,7 +667,7 @@ bool Notification_Class_Write_Property( iOffset += len; /* Increasing element of list */ - if(++idx >= NC_MAX_RECIPIENTS) { + 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; @@ -674,18 +676,217 @@ bool Notification_Class_Write_Property( /* Decoded all recipient list */ /* copy elements from temporary object */ - for(idx = 0; idx < NC_MAX_RECIPIENTS; idx++) + 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; break; default: wp_data->error_class = ERROR_CLASS_PROPERTY; - wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; break; } return status; } + + +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_OFFNORMAL: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_OFFNORMAL]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_OFFNORMAL_MASKED) ? true : false; + break; + + case EVENT_STATE_FAULT: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_FAULT]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_FAULT_MASKED) ? true : false; + break; + + case EVENT_STATE_NORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + event_data->priority = + CurrentNotify->Priority[EVENT_STATE_NORMAL]; + event_data->ackRequired = (CurrentNotify->Ack_Required & + TRANSITION_TO_NORMAL) ? true : false; + break; + + default: // shouldn't happen + break; + } + + /* send notifications for active recipients */ + /* 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) == true) + { + 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; + + 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) { + /* 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) { + + } + } + } +} diff --git a/bacnet-stack/demo/object/nc.h b/bacnet-stack/demo/object/nc.h index c785b9de..d36825ea 100644 --- a/bacnet-stack/demo/object/nc.h +++ b/bacnet-stack/demo/object/nc.h @@ -25,17 +25,24 @@ #ifndef NC_H #define NC_H +#include "event.h" + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -#define NC_MAX_RECIPIENTS 10 +#define NC_RESCAN_RECIPIENTS_SECS 60 -#define RECIPIENT_TYPE_NOTINITIALIZED 0 -#define RECIPIENT_TYPE_DEVICE 1 -#define RECIPIENT_TYPE_ADDRESS 2 +/* max "length" of recipient_list */ +#define NC_MAX_RECIPIENTS 10 +/* Recipient types */ +typedef enum { + RECIPIENT_TYPE_NOTINITIALIZED = 0, + RECIPIENT_TYPE_DEVICE = 1, + RECIPIENT_TYPE_ADDRESS = 2 +} NC_RECIPIENT_TYPE; /* BACnetRecipient sructuer*/ @@ -97,6 +104,11 @@ typedef struct Notification_Class_info { bool Notification_Class_Write_Property( BACNET_WRITE_PROPERTY_DATA * wp_data); + void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA * event_data); + + void Notification_Class_find_recipient(void); + #ifdef __cplusplus } diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c index ce78d898..b102b2aa 100644 --- a/bacnet-stack/demo/server/main.c +++ b/bacnet-stack/demo/server/main.c @@ -66,29 +66,6 @@ #include "bacfile.h" #endif -/* All included BACnet objects */ -static object_functions_t Object_Table[] = { - {DEVICE_OBJ_FUNCTIONS}, - {ANALOG_INPUT_OBJ_FUNCTIONS}, - {ANALOG_OUTPUT_OBJ_FUNCTIONS}, - {ANALOG_VALUE_OBJ_FUNCTIONS}, - {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}, - {MULTI_STATE_INPUT_OBJ_FUNCTIONS}, - {TRENDLOG_OBJ_FUNCTIONS}, -#if defined(BACFILE) - {FILE_OBJ_FUNCTIONS}, -#endif - {MAX_BACNET_OBJECT_TYPE, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL} -}; /** @file server/main.c Example server application using the BACnet Stack. */ @@ -106,10 +83,12 @@ static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; static void Init_Service_Handlers( void) { - Device_Init(&Object_Table[0]); + Device_Init(NULL); /* we need to handle who-is to support dynamic device binding */ apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* handle i-am to support binding to other devices */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, handler_i_am_bind); /* set the handler for all the services we don't implement */ /* It is required to send the proper reject message... */ apdu_set_unrecognized_service_handler_handler @@ -182,6 +161,8 @@ int main( time_t current_seconds = 0; uint32_t elapsed_seconds = 0; uint32_t elapsed_milliseconds = 0; + uint32_t address_binding_tmr = 0; + uint32_t recipient_scan_tmr = 0; /* allow the device ID to be set */ if (argc > 1) @@ -224,7 +205,21 @@ int main( } tsm_timer_milliseconds(elapsed_milliseconds); trend_log_timer(elapsed_seconds); + Device_local_reporting(elapsed_seconds * 1000); } + /* scan cache address */ + address_binding_tmr += elapsed_seconds; + if (address_binding_tmr >= 60) { + address_cache_timer(address_binding_tmr); + address_binding_tmr = 0; + } + /* try to find addresses of recipients */ + recipient_scan_tmr += elapsed_seconds; + if (recipient_scan_tmr >= NC_RESCAN_RECIPIENTS_SECS) { + Notification_Class_find_recipient(); + recipient_scan_tmr = 0; + } + /* output */ /* blink LEDs, Turn on or off outputs, etc */ diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index 1b9d3f7b..a2dc66af 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -393,6 +393,12 @@ typedef enum { /* do the MAX as define rather than enumeration for devices and compilers that want to use smaller datatype for enum */ #define MAX_BACNET_PROPERTY_ID 4194303 + +typedef enum { + EVENT_LOW_LIMIT_ENABLE = 1, + EVENT_HIGH_LIMIT_ENABLE = 2 +} BACNET_LIMIT_ENABLE; + typedef enum { ACTION_DIRECT = 0, ACTION_REVERSE = 1 @@ -420,6 +426,12 @@ typedef enum { EVENT_STATE_LOW_LIMIT = 4 } BACNET_EVENT_STATE; +typedef enum { + EVENT_ENABLE_TO_OFFNORMAL = 1, + EVENT_ENABLE_TO_FAULT = 2, + EVENT_ENABLE_TO_NORMAL = 4 +} BACNET_EVENT_ENABLE; + typedef enum { STATUS_OPERATIONAL = 0, STATUS_OPERATIONAL_READ_ONLY = 1,