From f4637325ade0ab052ed63c970afea081ea686f5d Mon Sep 17 00:00:00 2001 From: Tomasz Kazimierz Motyl Date: Tue, 16 Jul 2024 14:16:33 +0100 Subject: [PATCH] Added Binary Input and Binary Value intrinsic reporting CHANGE_OF_STATE algorithm as per 13.3.2 as per ASHRAE 135-2020 (#689) --- src/bacnet/basic/object/bi.c | 833 ++++++++++++++++++++- src/bacnet/basic/object/bi.h | 46 +- src/bacnet/basic/object/bv.c | 824 +++++++++++++++++++- src/bacnet/basic/object/bv.h | 48 ++ test/bacnet/basic/object/bi/CMakeLists.txt | 3 + test/bacnet/basic/object/bi/stubs.c | 67 ++ test/bacnet/basic/object/bv/CMakeLists.txt | 3 + test/bacnet/basic/object/bv/stubs.c | 67 ++ 8 files changed, 1883 insertions(+), 8 deletions(-) create mode 100644 test/bacnet/basic/object/bi/stubs.c create mode 100644 test/bacnet/basic/object/bv/stubs.c diff --git a/src/bacnet/basic/object/bi.c b/src/bacnet/basic/object/bi.c index ddce897a..fd2dceb1 100644 --- a/src/bacnet/basic/object/bi.c +++ b/src/bacnet/basic/object/bi.c @@ -16,6 +16,8 @@ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacdcode.h" +#include "bacnet/bacenum.h" +#include "bacnet/bactext.h" #include "bacnet/bacapp.h" #include "bacnet/rp.h" #include "bacnet/wp.h" @@ -26,6 +28,11 @@ /* me! */ #include "bacnet/basic/object/bi.h" +#include "bacnet/basic/sys/debug.h" +#if !defined(PRINT) +#define PRINT debug_perror +#endif + static const char *Default_Active_Text = "Active"; static const char *Default_Inactive_Text = "Inactive"; struct object_data { @@ -34,11 +41,26 @@ struct object_data { bool Present_Value : 1; bool Polarity : 1; bool Write_Enabled : 1; + unsigned Event_State:3; uint8_t Reliability; const char *Object_Name; const char *Active_Text; const char *Inactive_Text; const char *Description; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + unsigned Event_Enable:3; + unsigned Event_Detection_Enable:1; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification information */ + ACK_NOTIFICATION Ack_notify_data; + BACNET_BINARY_PV Alarm_Value; +#endif }; /* Key List for storing the object data sorted by instance number */ static OS_Keylist Object_List; @@ -55,9 +77,16 @@ static const int Properties_Required[] = { PROP_OUT_OF_SERVICE, PROP_POLARITY, -1 }; -static const int Properties_Optional[] = { PROP_RELIABILITY, PROP_DESCRIPTION, - PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, - -1 }; +static const int Properties_Optional[] = { PROP_RELIABILITY, + PROP_DESCRIPTION, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + PROP_TIME_DELAY, PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUE, + PROP_EVENT_ENABLE, PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, PROP_EVENT_TIME_STAMPS, + PROP_EVENT_DETECTION_ENABLE, +#endif + -1 }; static const int Properties_Proprietary[] = { -1 }; @@ -114,7 +143,7 @@ bool Binary_Input_Valid_Instance(uint32_t object_instance) /** * @brief Determines the number of objects - * @return Number of Analog Value objects + * @return Number of Binary Input objects */ unsigned Binary_Input_Count(void) { @@ -269,8 +298,7 @@ bool Binary_Input_Out_Of_Service(uint32_t object_instance) } /** - * @brief For a given object instance-number, sets the out-of-service property - * value + * @brief For a given object instance-number, sets the out-of-service property value * @param object_instance - object-instance number of the object * @param value - boolean out-of-service value * @return true if the out-of-service property value was set @@ -736,6 +764,55 @@ bool Binary_Input_Active_Text_Set(uint32_t object_instance, char *new_name) return status; } +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) +/** + * @brief Encode a EventTimeStamps property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Binary_Input_Event_Time_Stamps_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0, len = 0; + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (pObject) { + if (index < MAX_BACNET_EVENT_TRANSITION) { + len = encode_opening_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_date( + apdu, &pObject->Event_Time_Stamps[index].date); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time( + apdu, &pObject->Event_Time_Stamps[index].time); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} +#endif + /** * ReadProperty handler for this object. For the given ReadProperty * data, the application_data is loaded or the error flags are set. @@ -753,12 +830,25 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_CHARACTER_STRING char_string; uint8_t *apdu = NULL; bool state = false; + struct object_data *pObject; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + int apdu_size = 0; +#endif if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } + if(!(pObject = Binary_Input_Object(rpdata->object_instance))) { +#if !(defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING)) + (void) pObject; +#endif + return BACNET_STATUS_ERROR; + } apdu = rpdata->application_data; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + apdu_size = rpdata->application_data_len; +#endif switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( @@ -825,6 +915,73 @@ int Binary_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_character_string(&apdu[0], &char_string); break; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + case PROP_ALARM_VALUE: + /* note: you need to look up the actual value */ + apdu_len = encode_application_enumerated( + &apdu[0], pObject->Alarm_Value); + break; + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], pObject->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = encode_application_unsigned( + &apdu[0], pObject->Notification_Class); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true + : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true + : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_DETECTION_ENABLE: + apdu_len = + encode_application_boolean(&apdu[0], pObject->Event_Detection_Enable); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = encode_application_enumerated( + &apdu[0], pObject->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Binary_Input_Event_Time_Stamps_Encode, + MAX_BACNET_EVENT_TRANSITION, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -855,6 +1012,7 @@ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) bool status = false; /* return value */ int len = 0; BACNET_APPLICATION_DATA_VALUE value; + struct object_data *pObject; /* decode the some of the request */ len = bacapp_decode_application_data( @@ -866,6 +1024,12 @@ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } + if(!(pObject = Binary_Input_Object(wp_data->object_instance))) { +#if (!BINARY_INPUT_INTRINSIC_REPORTING) + (void) pObject; +#endif + return BACNET_STATUS_ERROR; + } /* only array properties can have array options */ if (wp_data->array_index != BACNET_ARRAY_ALL) { wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -905,6 +1069,73 @@ bool Binary_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + pObject->Time_Delay = value.type.Unsigned_Int; + pObject->Remaining_Time_Delay = pObject->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + pObject->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_ALARM_VALUE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= MAX_BINARY_PV) { + Binary_Input_Alarm_Value_Set(wp_data->object_instance, + (BACNET_BINARY_PV) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + + case PROP_EVENT_ENABLE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); + if (status) { + if (value.type.Bit_String.bits_used == 3) { + pObject->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 = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + switch ((BACNET_NOTIFY_TYPE)value.type.Enumerated) { + case NOTIFY_EVENT: + pObject->Notify_Type = 1; + break; + case NOTIFY_ALARM: + pObject->Notify_Type = 0; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + break; + } + } + break; +#endif default: if (property_lists_member( Properties_Required, Properties_Optional, @@ -1001,6 +1232,9 @@ uint32_t Binary_Input_Create(uint32_t object_instance) if (!pObject) { pObject = calloc(1, sizeof(struct object_data)); if (pObject) { +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + unsigned j; +#endif pObject->Object_Name = NULL; pObject->Description = NULL; pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; @@ -1011,6 +1245,26 @@ uint32_t Binary_Input_Create(uint32_t object_instance) pObject->Change_Of_Value = false; pObject->Write_Enabled = false; pObject->Polarity = false; +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + pObject->Event_State = EVENT_STATE_NORMAL; + pObject->Event_Detection_Enable = true; + /* notification class not connected */ + pObject->Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&pObject->Event_Time_Stamps[j]); + pObject->Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + Object_Type, Binary_Input_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(Object_Type, Binary_Input_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set( + Object_Type, Binary_Input_Alarm_Summary); +#endif /* add to list */ index = Keylist_Data_Add(Object_List, object_instance, pObject); if (index < 0) { @@ -1072,3 +1326,570 @@ void Binary_Input_Init(void) Object_List = Keylist_Create(); } } + + +/** + * For a given object instance-number, gets the event-state property value + * + * @param object_instance - object-instance number of the object + * + * @return event-state property value + */ +unsigned Binary_Input_Event_State(uint32_t object_instance) +{ + unsigned state = EVENT_STATE_NORMAL; +#if !(defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING)) + (void) object_instance; +#else + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (pObject) { + state = pObject->Event_State; + } +#endif + + return state; +} + +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) +/** + * For a given object instance-number, gets the event-detection-enable property value + * + * @param object_instance - object-instance number of the object + * + * @return event-detection-enable property value + */ +bool Binary_Input_Event_Detection_Enable(uint32_t object_instance) +{ + bool retval = false; +#if !(defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING)) + (void) object_instance; +#else + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (pObject) { + retval = pObject->Event_Detection_Enable; + } +#endif + + return retval; +} + +/** + * For a given object instance-number, sets the event-detection-enable property value + * + * @param object_instance - object-instance number of the object + * + * @return event-detection-enable property value + */ +bool Binary_Input_Event_Detection_Enable_Set(uint32_t object_instance, bool value) +{ + bool retval = false; +#if !(defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING)) + (void) object_instance; + (void) value; +#else + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (pObject) { + pObject->Event_Detection_Enable = value; + retval = true; + } +#endif + + return retval; +} + +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) +/** + * @brief Gets an object from the list using its index in the list + * @param index - index of the object in the list + * @return object found in the list, or NULL if not found + */ +static struct object_data *Binary_Input_Object_Index(int index) +{ + return Keylist_Data_Index(Object_List, index); +} +#endif + +int Binary_Input_Event_Information( + unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) +{ + struct object_data *pObject = Binary_Input_Object_Index(index); + + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + /* check index */ + if (pObject) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (pObject->Event_State != EVENT_STATE_NORMAL); + + /* Acked_Transitions property, which has at least one of the bits + (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ + IsNotAckedTransitions = + (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == + false) | + (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | + (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = Object_Type; + getevent_data->objectIdentifier.instance = + Binary_Input_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = pObject->Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + /* Event Time Stamps */ + for (i = 0; i < 3; i++) { + getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; + getevent_data->eventTimeStamps[i].value.dateTime = + pObject->Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = pObject->Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + /* Event Priorities */ + Notification_Class_Get_Priorities( + pObject->Notification_Class, getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Binary_Input_Alarm_Ack( + BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) +{ + struct object_data *pObject = NULL; + + if (!alarmack_data) { + return -1; + } + pObject = Binary_Input_Object(alarmack_data->eventObjectIdentifier.instance); + + if (!pObject) { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + if (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked == false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + pObject->Ack_notify_data.bSendAckNotify = true; + pObject->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + return 1; +} + +int Binary_Input_Alarm_Summary( + unsigned index, BACNET_GET_ALARM_SUMMARY_DATA *getalarm_data) +{ + struct object_data *pObject = Binary_Input_Object_Index(index); + + if(getalarm_data == NULL) { + PRINT("[%s %d]: NULL pointer parameter! getalarm_data = %p\r\n", __FILE__, __LINE__, (void *) getalarm_data); + return -2; + } + + /* check index */ + if (pObject) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if ((pObject->Event_State != EVENT_STATE_NORMAL) && + (pObject->Notify_Type == NOTIFY_ALARM)) { + /* Object Identifier */ + getalarm_data->objectIdentifier.type = Object_Type; + getalarm_data->objectIdentifier.instance = + Binary_Input_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = pObject->Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + return 1; /* active alarm */ + } else + return 0; /* no active alarm at this index */ + } else + return -1; /* end of list */ +} + +bool Binary_Input_Alarm_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV value) +{ + bool status = false; + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (pObject) { + if (pObject->Polarity != POLARITY_NORMAL) { + value = (value == BINARY_INACTIVE) ? BINARY_ACTIVE : BINARY_INACTIVE; + } + pObject->Alarm_Value = value; + status = true; + } + + return status; +} +#endif /* (INTRINSIC_REPORTING) */ + +void Binary_Input_Intrinsic_Reporting(uint32_t object_instance) +{ +#if !(defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING)) + (void) object_instance; +#else + BACNET_EVENT_NOTIFICATION_DATA event_data = { 0 }; + BACNET_CHARACTER_STRING msgText = { 0 }; + uint8_t FromState = 0; + uint8_t ToState = 0; + BACNET_BINARY_PV PresentVal = BINARY_INACTIVE; + bool SendNotify = false; + struct object_data *pObject = Binary_Input_Object(object_instance); + + if (!pObject) { + return; + } + + /* check whether Intrinsic reporting is enabled */ + if (!pObject->Event_Detection_Enable) { + return; /* limits are not configured */ + } + + if (pObject->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + pObject->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = pObject->Ack_notify_data.EventState; + PRINT("Binary-Input[%d]: Send AckNotification.\n", object_instance); + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* actual Present_Value */ + PresentVal = Binary_Input_Present_Value(object_instance); + FromState = pObject->Event_State; + switch (pObject->Event_State) { + case EVENT_STATE_NORMAL: + /* (a) If pCurrentState is NORMAL, and pMonitoredValue is equal to any of the values contained in pAlarmValues for + pTimeDelay, then indicate a transition to the OFFNORMAL event state. + */ + if ((PresentVal == pObject->Alarm_Value) && + ((pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!pObject->Remaining_Time_Delay) + pObject->Event_State = EVENT_STATE_OFFNORMAL; + else + pObject->Remaining_Time_Delay--; + break; + } + + /* value of the object is still in the same event state */ + pObject->Remaining_Time_Delay = pObject->Time_Delay; + break; + + case EVENT_STATE_OFFNORMAL: + /* (b) If pCurrentState is OFFNORMAL, and pMonitoredValue is not equal to any of the values contained in pAlarmValues + for pTimeDelayNormal, then indicate a transition to the NORMAL event state. + */ + if ((PresentVal != pObject->Alarm_Value) && + ((pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!pObject->Remaining_Time_Delay) + pObject->Event_State = EVENT_STATE_NORMAL; + else + pObject->Remaining_Time_Delay--; + break; + } + + /* value of the object is still in the same event state */ + pObject->Remaining_Time_Delay = pObject->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = pObject->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_NORMAL: + characterstring_init_ansi( &msgText, "Back to normal state from off-normal"); + break; + + case EVENT_STATE_OFFNORMAL: + characterstring_init_ansi( &msgText, "Back to off-normal state from normal"); + break; + + default: + break; + } /* switch (ToState) */ + PRINT("Binary-Input[%d]: Event_State goes from %.128s to %.128s.\n", + object_instance, bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); + /* Notify Type */ + event_data.notifyType = pObject->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = Object_Type; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + datetime_local( + &event_data.timeStamp.value.dateTime.date, + &event_data.timeStamp.value.dateTime.time, NULL, NULL); + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_OFFNORMAL: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_FAULT: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_FAULT], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_NORMAL: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_NORMAL], + &event_data.timeStamp.value.dateTime); + break; + default: + break; + } + } else { + /* fill event_data timeStamp */ + switch (ToState) { + case EVENT_STATE_FAULT: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_FAULT]); + break; + case EVENT_STATE_NORMAL: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_NORMAL]); + break; + case EVENT_STATE_OFFNORMAL: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL]); + break; + default: + break; + } + } + + /* Notification Class */ + event_data.notificationClass = pObject->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_CHANGE_OF_STATE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = pObject->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.changeOfState.newState = + (BACNET_PROPERTY_STATE) { .tag = PROP_STATE_BINARY_VALUE, .state = { .binaryValue = pObject->Present_Value } }; + /* Status_Flags of the referenced object. */ + bitstring_init( + &event_data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_IN_ALARM, + pObject->Event_State != EVENT_STATE_NORMAL); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, pObject->Out_Of_Service); + } + + /* add data from notification class */ + PRINT("Binary-Input[%d]: Notification Class[%d]-%s " + "%u/%u/%u-%u:%u:%u.%u!\n", + object_instance, event_data.notificationClass, + bactext_event_type_name(event_data.eventType), + (unsigned)event_data.timeStamp.value.dateTime.date.year, + (unsigned)event_data.timeStamp.value.dateTime.date.month, + (unsigned)event_data.timeStamp.value.dateTime.date.day, + (unsigned)event_data.timeStamp.value.dateTime.time.hour, + (unsigned)event_data.timeStamp.value.dateTime.time.min, + (unsigned)event_data.timeStamp.value.dateTime.time.sec, + (unsigned)event_data.timeStamp.value.dateTime.time.hundredths); + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + PRINT("Binary-Input[%d]: Ack Required!\n", object_instance); + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked = false; + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + false; + pObject->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .bIsAcked = false; + pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + default: /* shouldn't happen */ + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) */ +} + diff --git a/src/bacnet/basic/object/bi.h b/src/bacnet/basic/object/bi.h index 12b2e7c1..62a36fab 100644 --- a/src/bacnet/basic/object/bi.h +++ b/src/bacnet/basic/object/bi.h @@ -20,6 +20,13 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +#if (INTRINSIC_REPORTING) +#include "bacnet/basic/object/nc.h" +#include "bacnet/getevent.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/get_alarm_sum.h" +#endif + /** * @brief Callback for gateway write present value request * @param object_instance - object-instance number of the object @@ -121,6 +128,10 @@ extern "C" { uint32_t object_instance, bool value); + BACNET_STACK_EXPORT + unsigned Binary_Input_Event_State( + uint32_t object_instance); + BACNET_STACK_EXPORT bool Binary_Input_Encode_Value_List( uint32_t object_instance, @@ -141,7 +152,7 @@ extern "C" { BACNET_STACK_EXPORT void Binary_Input_Write_Present_Value_Callback_Set( binary_input_write_present_value_callback cb); - + BACNET_STACK_EXPORT uint32_t Binary_Input_Create( uint32_t object_instance); @@ -155,6 +166,39 @@ extern "C" { void Binary_Input_Init( void); +#if defined(INTRINSIC_REPORTING) && (BINARY_INPUT_INTRINSIC_REPORTING) + BACNET_STACK_EXPORT + bool Binary_Input_Event_Detection_Enable( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Binary_Input_Event_Detection_Enable_Set( + uint32_t object_instance, bool value); + + BACNET_STACK_EXPORT + int Binary_Input_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + + BACNET_STACK_EXPORT + int Binary_Input_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + + BACNET_STACK_EXPORT + int Binary_Input_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); + + BACNET_STACK_EXPORT + bool Binary_Input_Alarm_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV value); + + BACNET_STACK_EXPORT + void Binary_Input_Intrinsic_Reporting( + uint32_t object_instance); +#endif + + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/basic/object/bv.c b/src/bacnet/basic/object/bv.c index 0ca0855d..71fe20cf 100644 --- a/src/bacnet/basic/object/bv.c +++ b/src/bacnet/basic/object/bv.c @@ -16,14 +16,23 @@ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/bacdcode.h" +#include "bacnet/bacenum.h" +#include "bacnet/bactext.h" #include "bacnet/bacapp.h" #include "bacnet/wp.h" #include "bacnet/rp.h" +#include "bacnet/cov.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/sys/keylist.h" /* me! */ #include "bacnet/basic/object/bv.h" +#include "bacnet/basic/sys/debug.h" +#if !defined(PRINT) +#define PRINT debug_perror +#endif + static const char *Default_Active_Text = "Active"; static const char *Default_Inactive_Text = "Inactive"; struct object_data { @@ -32,11 +41,26 @@ struct object_data { bool Present_Value : 1; bool Write_Enabled : 1; bool Polarity : 1; + unsigned Event_State:3; uint8_t Reliability; const char *Object_Name; const char *Active_Text; const char *Inactive_Text; const char *Description; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + uint32_t Time_Delay; + uint32_t Notification_Class; + unsigned Event_Enable:3; + unsigned Event_Detection_Enable:1; + unsigned Notify_Type:1; + ACKED_INFO Acked_Transitions[MAX_BACNET_EVENT_TRANSITION]; + BACNET_DATE_TIME Event_Time_Stamps[MAX_BACNET_EVENT_TRANSITION]; + /* time to generate event notification */ + uint32_t Remaining_Time_Delay; + /* AckNotification information */ + ACK_NOTIFICATION Ack_notify_data; + BACNET_BINARY_PV Alarm_Value; +#endif }; /* Key List for storing the object data sorted by instance number */ static OS_Keylist Object_List; @@ -55,7 +79,15 @@ static const int Binary_Value_Properties_Required[] = { }; static const int Binary_Value_Properties_Optional[] = { - PROP_DESCRIPTION, PROP_RELIABILITY, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, -1 + PROP_DESCRIPTION, PROP_RELIABILITY, PROP_ACTIVE_TEXT, PROP_INACTIVE_TEXT, +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + PROP_TIME_DELAY, PROP_NOTIFICATION_CLASS, + PROP_ALARM_VALUE, + PROP_EVENT_ENABLE, PROP_ACKED_TRANSITIONS, + PROP_NOTIFY_TYPE, PROP_EVENT_TIME_STAMPS, + PROP_EVENT_DETECTION_ENABLE, +#endif + -1 }; static const int Binary_Value_Properties_Proprietary[] = { -1 }; @@ -741,6 +773,55 @@ bool Binary_Value_Inactive_Text_Set(uint32_t object_instance, char *new_name) return status; } +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) +/** + * @brief Encode a EventTimeStamps property element + * @param object_instance [in] BACnet network port object instance number + * @param index [in] array index requested: + * 0 to N for individual array members + * @param apdu [out] Buffer in which the APDU contents are built, or NULL to + * return the length of buffer if it had been built + * @return The length of the apdu encoded or + * BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX + */ +static int Binary_Value_Event_Time_Stamps_Encode( + uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu) +{ + int apdu_len = 0, len = 0; + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (pObject) { + if (index < MAX_BACNET_EVENT_TRANSITION) { + len = encode_opening_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_date( + apdu, &pObject->Event_Time_Stamps[index].date); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_application_time( + apdu, &pObject->Event_Time_Stamps[index].time); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, TIME_STAMP_DATETIME); + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} +#endif + /** * ReadProperty handler for this object. For the given ReadProperty * data, the application_data is loaded or the error flags are set. @@ -758,12 +839,25 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) BACNET_CHARACTER_STRING char_string; uint8_t *apdu = NULL; bool state = false; + struct object_data *pObject; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + int apdu_size = 0; +#endif if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { return 0; } + if(!(pObject = Binary_Value_Object(rpdata->object_instance))) { +#if !(defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING)) + (void) pObject; +#endif + return BACNET_STATUS_ERROR; + } apdu = rpdata->application_data; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + apdu_size = rpdata->application_data_len; +#endif switch (rpdata->object_property) { case PROP_OBJECT_IDENTIFIER: apdu_len = encode_application_object_id( @@ -830,6 +924,70 @@ int Binary_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) apdu_len = encode_application_character_string(&apdu[0], &char_string); break; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + case PROP_ALARM_VALUE: + /* note: you need to look up the actual value */ + apdu_len = encode_application_enumerated( + &apdu[0], pObject->Alarm_Value); + break; + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], pObject->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = encode_application_unsigned( + &apdu[0], pObject->Notification_Class); + break; + + case PROP_EVENT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_DETECTION_ENABLE: + apdu_len = + encode_application_boolean(&apdu[0], pObject->Event_Detection_Enable); + break; + + case PROP_ACKED_TRANSITIONS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_NOTIFY_TYPE: + apdu_len = encode_application_enumerated( + &apdu[0], pObject->Notify_Type ? NOTIFY_EVENT : NOTIFY_ALARM); + break; + + case PROP_EVENT_TIME_STAMPS: + apdu_len = bacnet_array_encode( + rpdata->object_instance, rpdata->array_index, + Binary_Value_Event_Time_Stamps_Encode, + MAX_BACNET_EVENT_TRANSITION, apdu, apdu_size); + if (apdu_len == BACNET_STATUS_ABORT) { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + } else if (apdu_len == BACNET_STATUS_ERROR) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + break; +#endif default: rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; @@ -860,6 +1018,7 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) bool status = false; /* return value */ int len = 0; BACNET_APPLICATION_DATA_VALUE value; + struct object_data *pObject; /* Valid data? */ if (wp_data == NULL) { @@ -879,6 +1038,12 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; return false; } + if(!(pObject = Binary_Value_Object(wp_data->object_instance))) { +#if (!BINARY_VALUE_INTRINSIC_REPORTING) + (void) pObject; +#endif + return BACNET_STATUS_ERROR; + } /* Only array properties can have array options. */ if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && (wp_data->array_index != BACNET_ARRAY_ALL)) { @@ -919,6 +1084,73 @@ bool Binary_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) } } break; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + pObject->Time_Delay = value.type.Unsigned_Int; + pObject->Remaining_Time_Delay = pObject->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + pObject->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_ALARM_VALUE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + if (value.type.Enumerated <= MAX_BINARY_PV) { + Binary_Value_Alarm_Value_Set(wp_data->object_instance, + (BACNET_BINARY_PV) value.type.Enumerated); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + + case PROP_EVENT_ENABLE: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_BIT_STRING); + if (status) { + if (value.type.Bit_String.bits_used == 3) { + pObject->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 = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + switch ((BACNET_NOTIFY_TYPE)value.type.Enumerated) { + case NOTIFY_EVENT: + pObject->Notify_Type = 1; + break; + case NOTIFY_ALARM: + pObject->Notify_Type = 0; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + status = false; + break; + } + } + break; +#endif default: if (property_lists_member( Binary_Value_Properties_Required, @@ -1017,6 +1249,9 @@ uint32_t Binary_Value_Create(uint32_t object_instance) if (!pObject) { pObject = calloc(1, sizeof(struct object_data)); if (pObject) { +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + unsigned j; +#endif pObject->Object_Name = NULL; pObject->Description = NULL; pObject->Reliability = RELIABILITY_NO_FAULT_DETECTED; @@ -1027,6 +1262,26 @@ uint32_t Binary_Value_Create(uint32_t object_instance) pObject->Change_Of_Value = false; pObject->Write_Enabled = false; pObject->Polarity = false; +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + pObject->Event_State = EVENT_STATE_NORMAL; + pObject->Event_Detection_Enable = true; + /* notification class not connected */ + pObject->Notification_Class = BACNET_MAX_INSTANCE; + /* initialize Event time stamps using wildcards and set Acked_transitions */ + for (j = 0; j < MAX_BACNET_EVENT_TRANSITION; j++) { + datetime_wildcard_set(&pObject->Event_Time_Stamps[j]); + pObject->Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + Object_Type, Binary_Value_Event_Information); + /* Set handler for AcknowledgeAlarm function */ + handler_alarm_ack_set(Object_Type, Binary_Value_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set( + Object_Type, Binary_Value_Alarm_Summary); +#endif /* add to list */ index = Keylist_Data_Add(Object_List, object_instance, pObject); if (index < 0) { @@ -1086,3 +1341,570 @@ void Binary_Value_Init(void) Object_List = Keylist_Create(); } } + +/** + * For a given object instance-number, gets the event-state property value + * + * @param object_instance - object-instance number of the object + * + * @return event-state property value + */ +unsigned Binary_Value_Event_State(uint32_t object_instance) +{ + unsigned state = EVENT_STATE_NORMAL; +#if !defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + (void) object_instance; +#else + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (pObject) { + state = pObject->Event_State; + } +#endif + + return state; +} + +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) +/** + * For a given object instance-number, gets the event-detection-enable property value + * + * @param object_instance - object-instance number of the object + * + * @return event-detection-enable property value + */ +bool Binary_Value_Event_Detection_Enable(uint32_t object_instance) +{ + bool retval = false; +#if !(defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING)) + (void) object_instance; +#else + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (pObject) { + retval = pObject->Event_Detection_Enable; + } +#endif + + return retval; +} + +/** + * For a given object instance-number, sets the event-detection-enable property value + * + * @param object_instance - object-instance number of the object + * + * @return event-detection-enable property value + */ +bool Binary_Value_Event_Detection_Enable_Set(uint32_t object_instance, bool value) +{ + bool retval = false; +#if !(defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING)) + (void) object_instance; + (void) value; +#else + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (pObject) { + pObject->Event_Detection_Enable = value; + retval = true; + } +#endif + + return retval; +} + +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) +/** + * @brief Gets an object from the list using its index in the list + * @param index - index of the object in the list + * @return object found in the list, or NULL if not found + */ +static struct object_data *Binary_Value_Object_Index(int index) +{ + return Keylist_Data_Index(Object_List, index); +} +#endif + +int Binary_Value_Event_Information( + unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) +{ + struct object_data *pObject = Binary_Value_Object_Index(index); + + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + /* check index */ + if (pObject) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (pObject->Event_State != EVENT_STATE_NORMAL); + + /* Acked_Transitions property, which has at least one of the bits + (TO-OFFNORMAL, TO-FAULT, TONORMAL) set to FALSE. */ + IsNotAckedTransitions = + (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked == + false) | + (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | + (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = Object_Type; + getevent_data->objectIdentifier.instance = + Binary_Value_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = pObject->Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + /* Event Time Stamps */ + for (i = 0; i < 3; i++) { + getevent_data->eventTimeStamps[i].tag = TIME_STAMP_DATETIME; + getevent_data->eventTimeStamps[i].value.dateTime = + pObject->Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = pObject->Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (pObject->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true : false); + /* Event Priorities */ + Notification_Class_Get_Priorities( + pObject->Notification_Class, getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Binary_Value_Alarm_Ack( + BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) +{ + struct object_data *pObject = NULL; + + if (!alarmack_data) { + return -1; + } + pObject = Binary_Value_Object(alarmack_data->eventObjectIdentifier.instance); + + if (!pObject) { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + if (pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked == false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false) { + if (alarmack_data->eventTimeStamp.tag != TIME_STAMP_DATETIME) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + if (datetime_compare( + &pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + /* Send ack notification */ + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + pObject->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + pObject->Ack_notify_data.bSendAckNotify = true; + pObject->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + return 1; +} + + +int Binary_Value_Alarm_Summary( + unsigned index, BACNET_GET_ALARM_SUMMARY_DATA *getalarm_data) +{ + struct object_data *pObject = Binary_Value_Object_Index(index); + + if(getalarm_data == NULL) { + PRINT("[%s %d]: NULL pointer parameter! getalarm_data = %p\r\n", __FILE__, __LINE__, (void *) getalarm_data); + return -2; + } + + /* check index */ + if (pObject) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if ((pObject->Event_State != EVENT_STATE_NORMAL) && + (pObject->Notify_Type == NOTIFY_ALARM)) { + /* Object Identifier */ + getalarm_data->objectIdentifier.type = Object_Type; + getalarm_data->objectIdentifier.instance = + Binary_Value_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = pObject->Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + pObject->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked); + return 1; /* active alarm */ + } else + return 0; /* no active alarm at this index */ + } else + return -1; /* end of list */ +} + +bool Binary_Value_Alarm_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV value) +{ + bool status = false; + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (pObject) { + if (pObject->Polarity != POLARITY_NORMAL) { + value = (value == BINARY_INACTIVE) ? BINARY_ACTIVE : BINARY_INACTIVE; + } + pObject->Alarm_Value = value; + status = true; + } + + return status; +} +#endif /* (INTRINSIC_REPORTING) */ + +void Binary_Value_Intrinsic_Reporting(uint32_t object_instance) +{ +#if !(defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING)) + (void) object_instance; +#else + BACNET_EVENT_NOTIFICATION_DATA event_data = { 0 }; + BACNET_CHARACTER_STRING msgText = { 0 }; + uint8_t FromState = 0; + uint8_t ToState = 0; + BACNET_BINARY_PV PresentVal = BINARY_INACTIVE; + bool SendNotify = false; + struct object_data *pObject = Binary_Value_Object(object_instance); + + if (!pObject) { + return; + } + + /* check whether Intrinsic reporting is enabled */ + if (!pObject->Event_Detection_Enable) { + return; /* limits are not configured */ + } + + if (pObject->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + pObject->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = pObject->Ack_notify_data.EventState; + PRINT("Binary-Input[%d]: Send AckNotification.\n", object_instance); + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* actual Present_Value */ + PresentVal = Binary_Value_Present_Value(object_instance); + FromState = pObject->Event_State; + switch (pObject->Event_State) { + case EVENT_STATE_NORMAL: + /* (a) If pCurrentState is NORMAL, and pMonitoredValue is equal to any of the values contained in pAlarmValues for + pTimeDelay, then indicate a transition to the OFFNORMAL event state. + */ + if ((PresentVal == pObject->Alarm_Value) && + ((pObject->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!pObject->Remaining_Time_Delay) + pObject->Event_State = EVENT_STATE_OFFNORMAL; + else + pObject->Remaining_Time_Delay--; + break; + } + + /* value of the object is still in the same event state */ + pObject->Remaining_Time_Delay = pObject->Time_Delay; + break; + + case EVENT_STATE_OFFNORMAL: + /* (b) If pCurrentState is OFFNORMAL, and pMonitoredValue is not equal to any of the values contained in pAlarmValues + for pTimeDelayNormal, then indicate a transition to the NORMAL event state. + */ + if ((PresentVal != pObject->Alarm_Value) && + ((pObject->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!pObject->Remaining_Time_Delay) + pObject->Event_State = EVENT_STATE_NORMAL; + else + pObject->Remaining_Time_Delay--; + break; + } + + /* value of the object is still in the same event state */ + pObject->Remaining_Time_Delay = pObject->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = pObject->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_NORMAL: + characterstring_init_ansi( &msgText, "Back to normal state from off-normal"); + break; + + case EVENT_STATE_OFFNORMAL: + characterstring_init_ansi( &msgText, "Back to off-normal state from normal"); + break; + + default: + break; + } /* switch (ToState) */ + PRINT("Binary-Input[%d]: Event_State goes from %.128s to %.128s.\n", + object_instance, bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); + /* Notify Type */ + event_data.notifyType = pObject->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = Object_Type; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + datetime_local( + &event_data.timeStamp.value.dateTime.date, + &event_data.timeStamp.value.dateTime.time, NULL, NULL); + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_OFFNORMAL: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_FAULT: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_FAULT], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_NORMAL: + datetime_copy( + &pObject->Event_Time_Stamps[TRANSITION_TO_NORMAL], + &event_data.timeStamp.value.dateTime); + break; + default: + break; + } + } else { + /* fill event_data timeStamp */ + switch (ToState) { + case EVENT_STATE_FAULT: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_FAULT]); + break; + case EVENT_STATE_NORMAL: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_NORMAL]); + break; + case EVENT_STATE_OFFNORMAL: + datetime_copy(&event_data.timeStamp.value.dateTime, + &pObject->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL]); + break; + default: + break; + } + } + + /* Notification Class */ + event_data.notificationClass = pObject->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_CHANGE_OF_STATE; + + /* Message Text */ + event_data.messageText = &msgText; + + /* Notify Type */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = pObject->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.changeOfState.newState = + (BACNET_PROPERTY_STATE) { .tag = PROP_STATE_BINARY_VALUE, .state = { .binaryValue = pObject->Present_Value } }; + /* Status_Flags of the referenced object. */ + bitstring_init( + &event_data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_IN_ALARM, + pObject->Event_State != EVENT_STATE_NORMAL); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &event_data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, pObject->Out_Of_Service); + } + + /* add data from notification class */ + PRINT("Binary-Value[%d]: Notification Class[%d]-%s " + "%u/%u/%u-%u:%u:%u.%u!\n", + object_instance, event_data.notificationClass, + bactext_event_type_name(event_data.eventType), + (unsigned)event_data.timeStamp.value.dateTime.date.year, + (unsigned)event_data.timeStamp.value.dateTime.date.month, + (unsigned)event_data.timeStamp.value.dateTime.date.day, + (unsigned)event_data.timeStamp.value.dateTime.time.hour, + (unsigned)event_data.timeStamp.value.dateTime.time.min, + (unsigned)event_data.timeStamp.value.dateTime.time.sec, + (unsigned)event_data.timeStamp.value.dateTime.time.hundredths); + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + PRINT("Binary-Value[%d]: Ack Required!\n", object_instance); + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked = false; + pObject->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + pObject->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + false; + pObject->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .bIsAcked = false; + pObject->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + default: /* shouldn't happen */ + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) */ +} + diff --git a/src/bacnet/basic/object/bv.h b/src/bacnet/basic/object/bv.h index aa9fd371..0c548e17 100644 --- a/src/bacnet/basic/object/bv.h +++ b/src/bacnet/basic/object/bv.h @@ -19,6 +19,12 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +#if (INTRINSIC_REPORTING) +#include "bacnet/basic/object/nc.h" +#include "bacnet/getevent.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/get_alarm_sum.h" +#endif /** * @brief Callback for gateway write present value request * @param object_instance - object-instance number of the object @@ -153,6 +159,48 @@ extern "C" { BACNET_STACK_EXPORT void Binary_Value_Cleanup( void); + BACNET_STACK_EXPORT + bool Binary_Value_Change_Of_Value( + uint32_t object_instance); + BACNET_STACK_EXPORT + void Binary_Value_Change_Of_Value_Clear( + uint32_t object_instance); + + BACNET_STACK_EXPORT + unsigned Binary_Input_Event_State( + uint32_t object_instance); + +#if defined(INTRINSIC_REPORTING) && (BINARY_VALUE_INTRINSIC_REPORTING) + BACNET_STACK_EXPORT + bool Binary_Value_Event_Detection_Enable( + uint32_t object_instance); + BACNET_STACK_EXPORT + bool Binary_Value_Event_Detection_Enable_Set( + uint32_t object_instance, bool value); + + BACNET_STACK_EXPORT + int Binary_Value_Event_Information( + unsigned index, + BACNET_GET_EVENT_INFORMATION_DATA * getevent_data); + + BACNET_STACK_EXPORT + int Binary_Value_Alarm_Ack( + BACNET_ALARM_ACK_DATA * alarmack_data, + BACNET_ERROR_CODE * error_code); + + BACNET_STACK_EXPORT + int Binary_Value_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); + + BACNET_STACK_EXPORT + bool Binary_Value_Alarm_Value_Set( + uint32_t object_instance, BACNET_BINARY_PV value); + + BACNET_STACK_EXPORT + void Binary_Value_Intrinsic_Reporting( + uint32_t object_instance); +#endif #ifdef __cplusplus } diff --git a/test/bacnet/basic/object/bi/CMakeLists.txt b/test/bacnet/basic/object/bi/CMakeLists.txt index 7306bd29..1fa382ef 100644 --- a/test/bacnet/basic/object/bi/CMakeLists.txt +++ b/test/bacnet/basic/object/bi/CMakeLists.txt @@ -23,6 +23,8 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + INTRINSIC_REPORTING=1 + BINARY_INPUT_INTRINSIC_REPORTING=1 ) include_directories( @@ -64,6 +66,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/sys/keylist.c # Test and test library files ./src/main.c + ./stubs.c ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c diff --git a/test/bacnet/basic/object/bi/stubs.c b/test/bacnet/basic/object/bi/stubs.c new file mode 100644 index 00000000..3b2aa854 --- /dev/null +++ b/test/bacnet/basic/object/bi/stubs.c @@ -0,0 +1,67 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include "bacnet/bacdef.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/datetime.h" +#include "bacnet/event.h" +#include "bacnet/getevent.h" +#include "bacnet/get_alarm_sum.h" +#include "bacnet/npdu.h" + +bool datetime_local( + BACNET_DATE *bdate, + BACNET_TIME *btime, + int16_t *utc_offset_minutes, + bool *dst_active) +{ + (void)bdate; + (void)btime; + (void)utc_offset_minutes; + (void)dst_active; + + return false; +} + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA *event_data) +{ + (void)event_data; +} + +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, uint32_t *pPriorityArray) +{ + (void)Object_Instance; + (void)pPriorityArray; +} + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, get_event_info_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, alarm_ack_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, get_alarm_summary_function pFunction) +{ + (void)object_type; + (void)pFunction; +} diff --git a/test/bacnet/basic/object/bv/CMakeLists.txt b/test/bacnet/basic/object/bv/CMakeLists.txt index 215070c2..24d9903c 100644 --- a/test/bacnet/basic/object/bv/CMakeLists.txt +++ b/test/bacnet/basic/object/bv/CMakeLists.txt @@ -23,6 +23,8 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + INTRINSIC_REPORTING=1 + BINARY_VALUE_INTRINSIC_REPORTING=1 ) include_directories( @@ -63,6 +65,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/sys/keylist.c # Test and test library files ./src/main.c + ./stubs.c ${TST_DIR}/bacnet/basic/object/property_test.c ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c diff --git a/test/bacnet/basic/object/bv/stubs.c b/test/bacnet/basic/object/bv/stubs.c new file mode 100644 index 00000000..3b2aa854 --- /dev/null +++ b/test/bacnet/basic/object/bv/stubs.c @@ -0,0 +1,67 @@ +/** + * @file + * @brief Stub functions for unit test of a BACnet object + * @author Steve Karg + * @date December 2022 + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include "bacnet/bacdef.h" +#include "bacnet/alarm_ack.h" +#include "bacnet/datetime.h" +#include "bacnet/event.h" +#include "bacnet/getevent.h" +#include "bacnet/get_alarm_sum.h" +#include "bacnet/npdu.h" + +bool datetime_local( + BACNET_DATE *bdate, + BACNET_TIME *btime, + int16_t *utc_offset_minutes, + bool *dst_active) +{ + (void)bdate; + (void)btime; + (void)utc_offset_minutes; + (void)dst_active; + + return false; +} + +void Notification_Class_common_reporting_function( + BACNET_EVENT_NOTIFICATION_DATA *event_data) +{ + (void)event_data; +} + +void Notification_Class_Get_Priorities( + uint32_t Object_Instance, uint32_t *pPriorityArray) +{ + (void)Object_Instance; + (void)pPriorityArray; +} + +void handler_get_event_information_set( + BACNET_OBJECT_TYPE object_type, get_event_info_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_alarm_ack_set( + BACNET_OBJECT_TYPE object_type, alarm_ack_function pFunction) +{ + (void)object_type; + (void)pFunction; +} + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, get_alarm_summary_function pFunction) +{ + (void)object_type; + (void)pFunction; +}