diff --git a/bacnet-stack/demo/BACnetDemo.workspace b/bacnet-stack/demo/BACnetDemo.workspace index 85d6420c..903aef37 100644 --- a/bacnet-stack/demo/BACnetDemo.workspace +++ b/bacnet-stack/demo/BACnetDemo.workspace @@ -1,12 +1,11 @@ - - - - - - - + + + + + + diff --git a/bacnet-stack/demo/handler/h_get_alarm_sum.c b/bacnet-stack/demo/handler/h_get_alarm_sum.c new file mode 100644 index 00000000..a406a79a --- /dev/null +++ b/bacnet-stack/demo/handler/h_get_alarm_sum.c @@ -0,0 +1,158 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include +#include + +#include "config.h" +#include "txbuf.h" +#include "bacdef.h" +#include "bacdcode.h" +#include "bacerror.h" +#include "apdu.h" +#include "npdu.h" +#include "abort.h" +#include "handlers.h" + +/** @file h_alarm_sum.c Handles Get Alarm Summary request. */ + +static get_alarm_summary_function Get_Alarm_Summary[MAX_BACNET_OBJECT_TYPE]; + +void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction) +{ + if (object_type < MAX_BACNET_OBJECT_TYPE) { + Get_Alarm_Summary[object_type] = pFunction; + } +} + +void handler_get_alarm_summary( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data) +{ + int len = 0; + int pdu_len = 0; + int apdu_len = 0; + int bytes_sent = 0; + int alarm_value = 0; + unsigned i = 0; + unsigned j = 0; + bool error = false; + BACNET_ADDRESS my_address; + BACNET_NPDU_DATA npdu_data; + BACNET_GET_ALARM_SUMMARY_DATA getalarm_data; + + + + /* encode the NPDU portion of the packet */ + datalink_get_my_address(&my_address); + npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL); + pdu_len = + npdu_encode_pdu(&Handler_Transmit_Buffer[0], src, &my_address, + &npdu_data); + if (service_data->segmented_message) { + /* we don't support segmentation - send an abort */ + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, + true); +#if PRINT_ENABLED + fprintf(stderr, + "GetAlarmSummary: Segmented message. Sending Abort!\n"); +#endif + goto GET_ALARM_SUMMARY_ABORT; + } + + /* init header */ + apdu_len = get_alarm_summary_ack_encode_apdu_init( + &Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id); + + + for (i = 0; i < MAX_BACNET_OBJECT_TYPE; i++) { + if (Get_Alarm_Summary[i]) { + for (j = 0; j < 0xffff; j++) { + alarm_value = Get_Alarm_Summary[i] (j, &getalarm_data); + if (alarm_value > 0) { + len = get_alarm_sumary_ack_encode_apdu_data( + &Handler_Transmit_Buffer[pdu_len + apdu_len], + service_data->max_resp - apdu_len, + &getalarm_data); + if (len <= 0) { + error = true; + goto GET_ALARM_SUMMARY_ERROR; + } + else + apdu_len += len; + } + else if (alarm_value < 0) { + break; + } + } + } + } + + +#if PRINT_ENABLED + fprintf(stderr, "GetAlarmSummary: Sending response!\n"); +#endif + +GET_ALARM_SUMMARY_ERROR: + if (error) { + if (len == BACNET_STATUS_ABORT) { + /* BACnet APDU too small to fit data, so proper response is Abort */ + apdu_len = + abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true); +#if PRINT_ENABLED + fprintf(stderr, + "GetAlarmSummary: Reply too big to fit into APDU!\n"); +#endif + } + else { + apdu_len = + bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len], + service_data->invoke_id, SERVICE_CONFIRMED_GET_ALARM_SUMMARY, + ERROR_CLASS_PROPERTY, ERROR_CODE_OTHER); +#if PRINT_ENABLED + fprintf(stderr, "GetAlarmSummary: Sending Error!\n"); +#endif + } + } + + + GET_ALARM_SUMMARY_ABORT: + pdu_len += apdu_len; + bytes_sent = + datalink_send_pdu(src, &npdu_data, &Handler_Transmit_Buffer[0], + pdu_len); + + return; +} diff --git a/bacnet-stack/demo/object/ai.c b/bacnet-stack/demo/object/ai.c index 2c9d80a3..f873a76f 100644 --- a/bacnet-stack/demo/object/ai.c +++ b/bacnet-stack/demo/object/ai.c @@ -131,6 +131,9 @@ void Analog_Input_Init( Analog_Input_Event_Information); /* Set handler for AcknowledgeAlarm function */ handler_alarm_ack_set(OBJECT_ANALOG_INPUT, Analog_Input_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set(OBJECT_ANALOG_INPUT, + Analog_Input_Alarm_Summary); #endif } } @@ -1118,6 +1121,44 @@ int Analog_Input_Alarm_Ack( return 1; } + +int Analog_Input_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data) +{ + + /* check index */ + if (index < MAX_ANALOG_INPUTS) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if((AI_Descr[index].Event_State != EVENT_STATE_NORMAL) && + (AI_Descr[index].Notify_Type == NOTIFY_ALARM)){ + /* Object Identifier */ + getalarm_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getalarm_data->objectIdentifier.instance = + Analog_Input_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = AI_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AI_Descr[index].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 */ +} #endif /* defined(INTRINSIC_REPORTING) */ diff --git a/bacnet-stack/demo/object/ai.h b/bacnet-stack/demo/object/ai.h index 24adfb3d..23daf740 100644 --- a/bacnet-stack/demo/object/ai.h +++ b/bacnet-stack/demo/object/ai.h @@ -35,6 +35,7 @@ #include "nc.h" #include "getevent.h" #include "alarm_ack.h" +#include "get_alarm_sum.h" #endif #ifdef __cplusplus @@ -124,6 +125,10 @@ extern "C" { int Analog_Input_Alarm_Ack( BACNET_ALARM_ACK_DATA * alarmack_data, BACNET_ERROR_CODE * error_code); + + int Analog_Input_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); #endif void Analog_Input_Init( diff --git a/bacnet-stack/demo/object/av.c b/bacnet-stack/demo/object/av.c index 9575bda1..a72ce4bc 100644 --- a/bacnet-stack/demo/object/av.c +++ b/bacnet-stack/demo/object/av.c @@ -134,6 +134,9 @@ void Analog_Value_Init( Analog_Value_Event_Information); /* Set handler for AcknowledgeAlarm function */ handler_alarm_ack_set(OBJECT_ANALOG_VALUE, Analog_Value_Alarm_Ack); + /* Set handler for GetAlarmSummary Service */ + handler_get_alarm_summary_set(OBJECT_ANALOG_VALUE, + Analog_Value_Alarm_Summary); #endif } } @@ -1220,6 +1223,44 @@ int Analog_Value_Alarm_Ack( /* Return OK */ return 1; } + +int Analog_Value_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data) +{ + + /* check index */ + if (index < MAX_ANALOG_VALUES) { + /* Event_State is not equal to NORMAL and + Notify_Type property value is ALARM */ + if((AV_Descr[index].Event_State != EVENT_STATE_NORMAL) && + (AV_Descr[index].Notify_Type == NOTIFY_ALARM)){ + /* Object Identifier */ + getalarm_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getalarm_data->objectIdentifier.instance = + Analog_Value_Index_To_Instance(index); + /* Alarm State */ + getalarm_data->alarmState = AV_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getalarm_data->acknowledgedTransitions); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_OFFNORMAL]. + bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getalarm_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AV_Descr[index].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 */ +} #endif /* defined(INTRINSIC_REPORTING) */ diff --git a/bacnet-stack/demo/object/av.h b/bacnet-stack/demo/object/av.h index 7e45cfde..e05f8886 100644 --- a/bacnet-stack/demo/object/av.h +++ b/bacnet-stack/demo/object/av.h @@ -36,6 +36,7 @@ #include "nc.h" #include "alarm_ack.h" #include "getevent.h" +#include "get_alarm_sum.h" #endif #ifdef __cplusplus @@ -113,6 +114,10 @@ extern "C" { int Analog_Value_Alarm_Ack( BACNET_ALARM_ACK_DATA * alarmack_data, BACNET_ERROR_CODE * error_code); + + int Analog_Value_Alarm_Summary( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); #endif void Analog_Value_Init( diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c index a5dee4fd..1e05dc8a 100644 --- a/bacnet-stack/demo/server/main.c +++ b/bacnet-stack/demo/server/main.c @@ -123,7 +123,9 @@ static void Init_Service_Handlers( handler_alarm_ack); apdu_set_confirmed_handler(SERVICE_CONFIRMED_GET_EVENT_INFORMATION, handler_get_event_information); -#endif /* defined(INTRINSIC_REPORTING) */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_GET_ALARM_SUMMARY, + handler_get_alarm_summary); +#endif /* defined(INTRINSIC_REPORTING) */ } /** Handler registered with atexit() inside main function to, well, cleanup. diff --git a/bacnet-stack/include/get_alarm_sum.h b/bacnet-stack/include/get_alarm_sum.h new file mode 100644 index 00000000..3f71d130 --- /dev/null +++ b/bacnet-stack/include/get_alarm_sum.h @@ -0,0 +1,84 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef BACNET_GET_ALARM_SUM_H_ +#define BACNET_GET_ALARM_SUM_H_ + +#include "bacenum.h" +#include +#include +#include "bacapp.h" +#include "timestamp.h" + + +typedef struct BACnet_Get_Alarm_Summary_Data { + BACNET_OBJECT_ID objectIdentifier; + BACNET_EVENT_STATE alarmState; + BACNET_BIT_STRING acknowledgedTransitions; +} BACNET_GET_ALARM_SUMMARY_DATA; + + +/* return 0 if no active alarm at this index + return -1 if end of list + return +1 if active alarm */ +typedef int ( + *get_alarm_summary_function) ( + unsigned index, + BACNET_GET_ALARM_SUMMARY_DATA * getalarm_data); + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + /* set GetAlarmSummary function */ + void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction); + + /* encode service */ + int get_alarm_summary_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id); + + int get_alarm_sumary_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @defgroup ALMEVNT Alarm and Event Management BIBBs + * @ingroup ALMEVNT + * 13.1 ConfirmedCOVNotification Service
+ * The GetAlarmSummary service is used by a client BACnet-user to obtain + * a summary of "active alarms." The term "active alarm" refers to + * BACnet standard objects that have an Event_State property whose value + * is not equal to NORMAL and a Notify_Type property whose value is ALARM. + * The GetEnrollmentSummary service provides a more sophisticated approach + * with various kinds of filters. + */ +#endif /* BACNET_GET_ALARM_SUM_H_ */ diff --git a/bacnet-stack/include/handlers.h b/bacnet-stack/include/handlers.h index f921083e..bb9f607e 100644 --- a/bacnet-stack/include/handlers.h +++ b/bacnet-stack/include/handlers.h @@ -37,6 +37,7 @@ #include "wp.h" #include "readrange.h" #include "getevent.h" +#include "get_alarm_sum.h" #include "alarm_ack.h" @@ -282,6 +283,16 @@ extern "C" { BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data); + void handler_get_alarm_summary_set( + BACNET_OBJECT_TYPE object_type, + get_alarm_summary_function pFunction); + + void handler_get_alarm_summary( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src, + BACNET_CONFIRMED_SERVICE_DATA * service_data); + #ifdef __cplusplus } diff --git a/bacnet-stack/lib/Makefile b/bacnet-stack/lib/Makefile index 19afb665..0bcd6e8c 100644 --- a/bacnet-stack/lib/Makefile +++ b/bacnet-stack/lib/Makefile @@ -57,6 +57,7 @@ CORE_SRC = \ $(BACNET_CORE)/alarm_ack.c \ $(BACNET_CORE)/event.c \ $(BACNET_CORE)/getevent.c \ + $(BACNET_CORE)/get_alarm_sum.c \ $(BACNET_CORE)/readrange.c \ $(BACNET_CORE)/timestamp.c \ $(BACNET_CORE)/version.c @@ -87,6 +88,7 @@ HANDLER_SRC = \ $(BACNET_HANDLER)/h_cov.c \ $(BACNET_HANDLER)/h_ucov.c \ $(BACNET_HANDLER)/h_getevent.c \ + $(BACNET_HANDLER)/h_get_alarm_sum.c \ $(BACNET_HANDLER)/h_pt.c \ $(BACNET_HANDLER)/h_pt_a.c \ $(BACNET_HANDLER)/h_upt.c \ diff --git a/bacnet-stack/src/get_alarm_sum.c b/bacnet-stack/src/get_alarm_sum.c new file mode 100644 index 00000000..6ffa5402 --- /dev/null +++ b/bacnet-stack/src/get_alarm_sum.c @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (C) 2011 Krzysztof Malorny +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include + +#include "bacdcode.h" +#include "get_alarm_sum.h" +#include "npdu.h" + + +int get_alarm_summary_ack_encode_apdu_init( + uint8_t * apdu, + uint8_t invoke_id) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ + apdu[1] = invoke_id; /* original invoke id from request */ + apdu[2] = SERVICE_CONFIRMED_GET_ALARM_SUMMARY; + apdu_len = 3; + } + + return apdu_len; +} + +int get_alarm_sumary_ack_encode_apdu_data( + uint8_t * apdu, + size_t max_apdu, + BACNET_GET_ALARM_SUMMARY_DATA * get_alarm_data) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (!apdu) { + apdu_len = BACNET_STATUS_ERROR; + } + else if(max_apdu >= 10) { + /* tag 0 - Object Identifier */ + apdu_len += encode_application_object_id(&apdu[apdu_len], + get_alarm_data->objectIdentifier.type, + get_alarm_data->objectIdentifier.instance); + /* tag 1 - Alarm State */ + apdu_len += encode_application_enumerated(&apdu[apdu_len], + get_alarm_data->alarmState); + /* tag 2 - Acknowledged Transitions */ + apdu_len += encode_application_bitstring(&apdu[apdu_len], + &get_alarm_data->acknowledgedTransitions); + } + else { + apdu_len = BACNET_STATUS_ABORT; + } + + return apdu_len; +}