From 9acca32ba8ce8764e5cc0d8da766b77480383be1 Mon Sep 17 00:00:00 2001 From: Greg Shue <32416235+gregshue@users.noreply.github.com> Date: Tue, 3 Aug 2021 07:12:45 -0700 Subject: [PATCH] Moved west.yml to root; updated to zephyr v2.6.0 (#186) Co-authored-by: Gregory Shue --- ports/zephyr/ai.c | 1352 +++++++++++ ports/zephyr/av.c | 1451 ++++++++++++ ports/zephyr/event.c | 2080 +++++++++++++++++ west.yml | 44 + zephyr/CMakeLists.txt | 7 +- zephyr/manifests/zp/zephyr/west.yml | 35 - zephyr/samples/hello_bacnet_stack/sample.yaml | 4 +- zephyr/samples/profiles/b-ss/sample.yaml | 4 +- zephyr/samples/profiles/b-ss/src/main.c | 8 +- 9 files changed, 4938 insertions(+), 47 deletions(-) create mode 100644 ports/zephyr/ai.c create mode 100644 ports/zephyr/av.c create mode 100644 ports/zephyr/event.c create mode 100644 west.yml delete mode 100644 zephyr/manifests/zp/zephyr/west.yml diff --git a/ports/zephyr/ai.c b/ports/zephyr/ai.c new file mode 100644 index 00000000..61b928f1 --- /dev/null +++ b/ports/zephyr/ai.c @@ -0,0 +1,1352 @@ +/************************************************************************** + * + * Copyright (C) 2005 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 + * "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. + * + *********************************************************************/ + +/* Analog Input Objects customize for your use */ + +#include +#include +#include + +#include "bacnet/bacdef.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacenum.h" +#include "bacnet/bactext.h" +#include "bacnet/config.h" /* the custom stuff */ +#include "bacnet/basic/object/device.h" +#include "bacnet/basic/services.h" +#include "bacnet/proplist.h" +#include "bacnet/timestamp.h" +#include "bacnet/basic/object/ai.h" + +#if PRINT_ENABLED +#include +#define PRINTF(...) fprintf(stderr,__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +#ifndef MAX_ANALOG_INPUTS +#define MAX_ANALOG_INPUTS 4 +#endif + +static ANALOG_INPUT_DESCR AI_Descr[MAX_ANALOG_INPUTS]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Properties_Required[] = { PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, + PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_UNITS, -1 }; + +static const int Properties_Optional[] = { PROP_DESCRIPTION, PROP_RELIABILITY, + PROP_COV_INCREMENT, +#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 }; + +static const int Properties_Proprietary[] = { 9997, 9998, 9999, -1 }; + +void Analog_Input_Property_Lists( + const int **pRequired, const int **pOptional, const int **pProprietary) +{ + if (pRequired) { + *pRequired = Properties_Required; + } + if (pOptional) { + *pOptional = Properties_Optional; + } + if (pProprietary) { + *pProprietary = Properties_Proprietary; + } + + return; +} + +void Analog_Input_Init(void) +{ + unsigned i; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + for (i = 0; i < MAX_ANALOG_INPUTS; i++) { + AI_Descr[i].Present_Value = 0.0f; + AI_Descr[i].Out_Of_Service = false; + AI_Descr[i].Units = UNITS_PERCENT; + AI_Descr[i].Reliability = RELIABILITY_NO_FAULT_DETECTED; + AI_Descr[i].Prior_Value = 0.0f; + AI_Descr[i].COV_Increment = 1.0f; + AI_Descr[i].Changed = false; +#if defined(INTRINSIC_REPORTING) + AI_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + AI_Descr[i].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(&AI_Descr[i].Event_Time_Stamps[j]); + AI_Descr[i].Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + OBJECT_ANALOG_INPUT, 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 + } +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need validate that the */ +/* given instance exists */ +bool Analog_Input_Valid_Instance(uint32_t object_instance) +{ + unsigned int index; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + return true; + } + + return false; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then count how many you have */ +unsigned Analog_Input_Count(void) +{ + return MAX_ANALOG_INPUTS; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the instance */ +/* that correlates to the correct index */ +uint32_t Analog_Input_Index_To_Instance(unsigned index) +{ + return index; +} + +/* we simply have 0-n object instances. Yours might be */ +/* more complex, and then you need to return the index */ +/* that correlates to the correct instance number */ +unsigned Analog_Input_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_INPUTS; + + if (object_instance < MAX_ANALOG_INPUTS) { + index = object_instance; + } + + return index; +} + +float Analog_Input_Present_Value(uint32_t object_instance) +{ + float value = 0.0; + unsigned int index; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].Present_Value; + } + + return value; +} + +static void Analog_Input_COV_Detect(unsigned int index, float value) +{ + float prior_value = 0.0; + float cov_increment = 0.0; + float cov_delta = 0.0; + + if (index < MAX_ANALOG_INPUTS) { + prior_value = AI_Descr[index].Prior_Value; + cov_increment = AI_Descr[index].COV_Increment; + if (prior_value > value) { + cov_delta = prior_value - value; + } else { + cov_delta = value - prior_value; + } + if (cov_delta >= cov_increment) { + AI_Descr[index].Changed = true; + AI_Descr[index].Prior_Value = value; + } + } +} + +void Analog_Input_Present_Value_Set(uint32_t object_instance, float value) +{ + unsigned int index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + Analog_Input_COV_Detect(index, value); + AI_Descr[index].Present_Value = value; + } +} + +bool Analog_Input_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + unsigned int index; + bool status = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + sprintf(text_string, "ANALOG INPUT %lu", (unsigned long)index); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/** + * 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 Analog_Input_Event_State(uint32_t object_instance) +{ + unsigned state = EVENT_STATE_NORMAL; + +#if defined(INTRINSIC_REPORTING) + unsigned index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + state = AI_Descr[index].Event_State; + } +#endif + + return state; +} + +bool Analog_Input_Change_Of_Value(uint32_t object_instance) +{ + unsigned index = 0; + bool changed = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + changed = AI_Descr[index].Changed; + } + + return changed; +} + +void Analog_Input_Change_Of_Value_Clear(uint32_t object_instance) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + AI_Descr[index].Changed = false; + } +} + +/** + * For a given object instance-number, loads the value_list with the COV data. + * + * @param object_instance - object-instance number of the object + * @param value_list - list of COV data + * + * @return true if the value list is encoded + */ +bool Analog_Input_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + bool in_alarm = false; + bool out_of_service = false; + const bool fault = false; + const bool overridden = false; + float present_value = 0.0; + unsigned index = 0; /* offset from instance lookup */ + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + if (AI_Descr[index].Event_State != EVENT_STATE_NORMAL) { + in_alarm = true; + } + out_of_service = AI_Descr[index].Out_Of_Service; + present_value = AI_Descr[index].Present_Value; + status = cov_value_list_encode_real(value_list, present_value, + in_alarm, fault, overridden, out_of_service); + } + + return status; +} + +float Analog_Input_COV_Increment(uint32_t object_instance) +{ + unsigned index = 0; + float value = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].COV_Increment; + } + + return value; +} + +void Analog_Input_COV_Increment_Set(uint32_t object_instance, float value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + AI_Descr[index].COV_Increment = value; + Analog_Input_COV_Detect(index, AI_Descr[index].Present_Value); + } +} + +bool Analog_Input_Out_Of_Service(uint32_t object_instance) +{ + unsigned index = 0; + bool value = false; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + value = AI_Descr[index].Out_Of_Service; + } + + return value; +} + +void Analog_Input_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + unsigned index = 0; + + index = Analog_Input_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_INPUTS) { + /* BACnet Testing Observed Incident oi00104 + The Changed flag was not being set when a client wrote to the + Out-of-Service bit. Revealed by BACnet Test Client v1.8.16 ( + www.bac-test.com/bacnet-test-client-download ) BC 135.1: 8.2.1-A BC + 135.1: 8.2.2-A Any discussions can be directed to edward@bac-test.com + Please feel free to remove this comment when my changes accepted after + suitable time for review by all interested parties. Say 6 months -> + September 2016 */ + if (AI_Descr[index].Out_Of_Service != value) { + AI_Descr[index].Changed = true; + } + AI_Descr[index].Out_Of_Service = value; + } +} + +/* return apdu length, or BACNET_STATUS_ERROR on error */ +/* assumption - object already exists */ +int Analog_Input_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + ANALOG_INPUT_DESCR *CurrentAI; + unsigned object_index = 0; +#if defined(INTRINSIC_REPORTING) + unsigned i = 0; + int len = 0; +#endif + uint8_t *apdu = NULL; + + if ((rpdata == NULL) || (rpdata->application_data == NULL) || + (rpdata->application_data_len == 0)) { + return 0; + } + + object_index = Analog_Input_Instance_To_Index(rpdata->object_instance); + if (object_index < MAX_ANALOG_INPUTS) { + CurrentAI = &AI_Descr[object_index]; + } else { + return BACNET_STATUS_ERROR; + } + + apdu = rpdata->application_data; + switch ((int)rpdata->object_property) { + case PROP_OBJECT_IDENTIFIER: + apdu_len = encode_application_object_id( + &apdu[0], OBJECT_ANALOG_INPUT, rpdata->object_instance); + break; + + case PROP_OBJECT_NAME: + case PROP_DESCRIPTION: + Analog_Input_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_INPUT); + break; + + case PROP_PRESENT_VALUE: + apdu_len = encode_application_real( + &apdu[0], Analog_Input_Present_Value(rpdata->object_instance)); + break; + + case PROP_STATUS_FLAGS: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, STATUS_FLAG_IN_ALARM, + Analog_Input_Event_State(rpdata->object_instance) != + EVENT_STATE_NORMAL); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAI->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: + apdu_len = + encode_application_enumerated(&apdu[0], + Analog_Input_Event_State(rpdata->object_instance)); + break; + + case PROP_RELIABILITY: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAI->Reliability); + break; + + case PROP_OUT_OF_SERVICE: + apdu_len = + encode_application_boolean(&apdu[0], CurrentAI->Out_Of_Service); + break; + + case PROP_UNITS: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAI->Units); + break; + + case PROP_COV_INCREMENT: + apdu_len = + encode_application_real(&apdu[0], CurrentAI->COV_Increment); + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + apdu_len = + encode_application_unsigned(&apdu[0], CurrentAI->Time_Delay); + break; + + case PROP_NOTIFICATION_CLASS: + apdu_len = encode_application_unsigned( + &apdu[0], CurrentAI->Notification_Class); + break; + + case PROP_HIGH_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAI->High_Limit); + break; + + case PROP_LOW_LIMIT: + apdu_len = encode_application_real(&apdu[0], CurrentAI->Low_Limit); + break; + + case PROP_DEADBAND: + apdu_len = encode_application_real(&apdu[0], CurrentAI->Deadband); + break; + + case PROP_LIMIT_ENABLE: + bitstring_init(&bit_string); + bitstring_set_bit(&bit_string, 0, + (CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) ? true + : false); + bitstring_set_bit(&bit_string, 1, + (CurrentAI->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, + (CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + (CurrentAI->Event_Enable & EVENT_ENABLE_TO_FAULT) ? true + : false); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + (CurrentAI->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, + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + CurrentAI->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], CurrentAI->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], MAX_BACNET_EVENT_TRANSITION); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) { + len = encode_opening_tag( + &apdu[apdu_len], TIME_STAMP_DATETIME); + len += encode_application_date(&apdu[apdu_len + len], + &CurrentAI->Event_Time_Stamps[i].date); + len += encode_application_time(&apdu[apdu_len + len], + &CurrentAI->Event_Time_Stamps[i].time); + len += encode_closing_tag( + &apdu[apdu_len + len], TIME_STAMP_DATETIME); + + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len = + encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + apdu_len += encode_application_date(&apdu[apdu_len], + &CurrentAI->Event_Time_Stamps[rpdata->array_index].date); + apdu_len += encode_application_time(&apdu[apdu_len], + &CurrentAI->Event_Time_Stamps[rpdata->array_index].time); + apdu_len += + encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + } else { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + apdu_len = BACNET_STATUS_ERROR; + } + break; +#endif + case 9997: + /* test case for real encoding-decoding real value correctly */ + apdu_len = encode_application_real(&apdu[0], 90.510F); + break; + case 9998: + /* test case for unsigned encoding-decoding unsigned value correctly + */ + apdu_len = encode_application_unsigned(&apdu[0], 90); + break; + case 9999: + /* test case for signed encoding-decoding negative value correctly + */ + apdu_len = encode_application_signed(&apdu[0], -200); + break; + default: + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/* returns true if successful */ +bool Analog_Input_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + ANALOG_INPUT_DESCR *CurrentAI; + + /* decode the some of the request */ + len = bacapp_decode_application_data( + wp_data->application_data, wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + /* only array properties can have array options */ + if ((wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + object_index = Analog_Input_Instance_To_Index(wp_data->object_instance); + if (object_index < MAX_ANALOG_INPUTS) { + CurrentAI = &AI_Descr[object_index]; + } else { + return false; + } + + switch ((int)wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + if (CurrentAI->Out_Of_Service == true) { + Analog_Input_Present_Value_Set( + wp_data->object_instance, value.type.Real); + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + status = false; + } + } + break; + + case PROP_OUT_OF_SERVICE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BOOLEAN); + if (status) { + Analog_Input_Out_Of_Service_Set( + wp_data->object_instance, value.type.Boolean); + } + break; + + case PROP_UNITS: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + CurrentAI->Units = value.type.Enumerated; + } + break; + + case PROP_COV_INCREMENT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + if (value.type.Real >= 0.0) { + Analog_Input_COV_Increment_Set( + wp_data->object_instance, value.type.Real); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + CurrentAI->Time_Delay = value.type.Unsigned_Int; + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + CurrentAI->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_HIGH_LIMIT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAI->High_Limit = value.type.Real; + } + break; + + case PROP_LOW_LIMIT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAI->Low_Limit = value.type.Real; + } + break; + + case PROP_DEADBAND: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAI->Deadband = value.type.Real; + } + break; + + case PROP_LIMIT_ENABLE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BIT_STRING); + if (status) { + if (value.type.Bit_String.bits_used == 2) { + CurrentAI->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 = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BIT_STRING); + if (status) { + if (value.type.Bit_String.bits_used == 3) { + CurrentAI->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: + CurrentAI->Notify_Type = 1; + break; + case NOTIFY_ALARM: + CurrentAI->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 + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: + case PROP_RELIABILITY: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif + case 9997: + case 9998: + case 9999: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +void Analog_Input_Intrinsic_Reporting(uint32_t object_instance) +{ +#if defined(INTRINSIC_REPORTING) + BACNET_EVENT_NOTIFICATION_DATA event_data = { 0 }; + BACNET_CHARACTER_STRING msgText = { 0 }; + ANALOG_INPUT_DESCR *CurrentAI = NULL; + unsigned int object_index = 0; + uint8_t FromState = 0; + uint8_t ToState = 0; + float ExceededLimit = 0.0f; + float PresentVal = 0.0f; + bool SendNotify = false; + + object_index = Analog_Input_Instance_To_Index(object_instance); + if (object_index < MAX_ANALOG_INPUTS) { + CurrentAI = &AI_Descr[object_index]; + } else { + return; + } + /* check limits */ + if (!CurrentAI->Limit_Enable) { + return; /* limits are not configured */ + } + + if (CurrentAI->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + CurrentAI->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = CurrentAI->Ack_notify_data.EventState; + PRINTF("Analog-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 = Analog_Input_Present_Value(object_instance); + FromState = CurrentAI->Event_State; + switch (CurrentAI->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 > CurrentAI->High_Limit) && + ((CurrentAI->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_HIGH_LIMIT; + else + CurrentAI->Remaining_Time_Delay--; + 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 < CurrentAI->Low_Limit) && + ((CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_LOW_LIMIT; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + 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 < + CurrentAI->High_Limit - CurrentAI->Deadband) && + ((CurrentAI->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_NORMAL; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + 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 > CurrentAI->Low_Limit + CurrentAI->Deadband) && + ((CurrentAI->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAI->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAI->Remaining_Time_Delay) + CurrentAI->Event_State = EVENT_STATE_NORMAL; + else + CurrentAI->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAI->Remaining_Time_Delay = CurrentAI->Time_Delay; + break; + + default: + return; /* shouldn't happen */ + } /* switch (FromState) */ + + ToState = CurrentAI->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 = CurrentAI->High_Limit; + characterstring_init_ansi(&msgText, "Goes to high limit"); + break; + + case EVENT_STATE_LOW_LIMIT: + ExceededLimit = CurrentAI->Low_Limit; + characterstring_init_ansi(&msgText, "Goes to low limit"); + break; + + case EVENT_STATE_NORMAL: + if (FromState == EVENT_STATE_HIGH_LIMIT) { + ExceededLimit = CurrentAI->High_Limit; + characterstring_init_ansi( + &msgText, "Back to normal state from high limit"); + } else { + ExceededLimit = CurrentAI->Low_Limit; + characterstring_init_ansi( + &msgText, "Back to normal state from low limit"); + } + break; + + default: + ExceededLimit = 0; + break; + } /* switch (ToState) */ + PRINTF("Analog-Input[%d]: Event_State goes from %s to %s.\n", + object_instance, + bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); + /* Notify Type */ + event_data.notifyType = CurrentAI->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + if (SendNotify) { + /* Event Object Identifier */ + event_data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + event_data.eventObjectIdentifier.instance = object_instance; + + /* Time Stamp */ + event_data.timeStamp.tag = TIME_STAMP_DATETIME; + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + Device_getCurrentDateTime(&event_data.timeStamp.value.dateTime); + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + datetime_copy( + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_FAULT: + datetime_copy( + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_FAULT], + &event_data.timeStamp.value.dateTime); + break; + case EVENT_STATE_NORMAL: + datetime_copy( + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_NORMAL], + &event_data.timeStamp.value.dateTime); + break; + default: + break; + } + } else { + /* fill event_data timeStamp */ + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + datetime_copy( + &event_data.timeStamp.value.dateTime, + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL]); + break; + case EVENT_STATE_FAULT: + datetime_copy( + &event_data.timeStamp.value.dateTime, + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_FAULT]); + break; + case EVENT_STATE_NORMAL: + datetime_copy( + &event_data.timeStamp.value.dateTime, + &CurrentAI->Event_Time_Stamps[TRANSITION_TO_NORMAL]); + break; + default: + break; + } + } + + /* Notification Class */ + event_data.notificationClass = CurrentAI->Notification_Class; + + /* Event Type */ + event_data.eventType = EVENT_OUT_OF_RANGE; + + /* 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 = CurrentAI->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.outOfRange.exceedingValue = + PresentVal; + /* Status_Flags of the referenced object. */ + bitstring_init( + &event_data.notificationParams.outOfRange.statusFlags); + bitstring_set_bit( + &event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, + CurrentAI->Event_State != EVENT_STATE_NORMAL); + 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, CurrentAI->Out_Of_Service); + /* Deadband used for limit checking. */ + event_data.notificationParams.outOfRange.deadband = + CurrentAI->Deadband; + /* Limit that was exceeded. */ + event_data.notificationParams.outOfRange.exceededLimit = + ExceededLimit; + } + + /* add data from notification class */ + PRINTF("Analog-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)) { + PRINTF("Analog-Input[%d]: Ack Required!\n", object_instance); + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked = false; + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + false; + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL] + .bIsAcked = false; + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + default: /* shouldn't happen */ + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) */ +} + +#if defined(INTRINSIC_REPORTING) +int Analog_Input_Event_Information( + unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) +{ + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + /* check index */ + if (index < MAX_ANALOG_INPUTS) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (AI_Descr[index].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 = + (AI_Descr[index] + .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked == false) | + (AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | + (AI_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = OBJECT_ANALOG_INPUT; + getevent_data->objectIdentifier.instance = + Analog_Input_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = AI_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AI_Descr[index] + .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AI_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AI_Descr[index].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 = + AI_Descr[index].Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = AI_Descr[index].Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_FAULT) ? true + : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (AI_Descr[index].Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true + : false); + /* Event Priorities */ + Notification_Class_Get_Priorities( + AI_Descr[index].Notification_Class, getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Analog_Input_Alarm_Ack( + BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) +{ + ANALOG_INPUT_DESCR *CurrentAI; + unsigned int object_index; + + object_index = Analog_Input_Instance_To_Index( + alarmack_data->eventObjectIdentifier.instance); + + if (object_index < MAX_ANALOG_INPUTS) + CurrentAI = &AI_Descr[object_index]; + else { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (CurrentAI->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( + &CurrentAI->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 */ + CurrentAI->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAI->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (CurrentAI->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( + &CurrentAI->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 */ + CurrentAI->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAI->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (CurrentAI->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( + &CurrentAI->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 */ + CurrentAI->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAI->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + CurrentAI->Ack_notify_data.bSendAckNotify = true; + CurrentAI->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + 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/ports/zephyr/av.c b/ports/zephyr/av.c new file mode 100644 index 00000000..edcb1e50 --- /dev/null +++ b/ports/zephyr/av.c @@ -0,0 +1,1451 @@ +/************************************************************************** + * + * 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 + * "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. + * + *********************************************************************/ + +/* Analog Value Objects - customize for your use */ + +#include +#include +#include +#include + +#include "bacnet/bacdef.h" +#include "bacnet/bacdcode.h" +#include "bacnet/bacenum.h" +#include "bacnet/bacapp.h" +#include "bacnet/bactext.h" +#include "bacnet/config.h" /* the custom stuff */ +#include "bacnet/basic/object/device.h" +#include "bacnet/basic/services.h" +#include "bacnet/basic/object/av.h" + +#ifndef MAX_ANALOG_VALUES +#define MAX_ANALOG_VALUES 4 +#endif + +static ANALOG_VALUE_DESCR AV_Descr[MAX_ANALOG_VALUES]; + +/* These three arrays are used by the ReadPropertyMultiple handler */ +static const int Analog_Value_Properties_Required[] = { PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_PRESENT_VALUE, PROP_STATUS_FLAGS, + PROP_EVENT_STATE, PROP_OUT_OF_SERVICE, PROP_UNITS, -1 }; + +static const int Analog_Value_Properties_Optional[] = { PROP_DESCRIPTION, + PROP_COV_INCREMENT, +#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 }; + +static const int Analog_Value_Properties_Proprietary[] = { -1 }; + +/** + * Initialize the pointers for the required, the optional and the properitary + * value properties. + * + * @param pRequired - Pointer to the pointer of required values. + * @param pOptional - Pointer to the pointer of optional values. + * @param pProprietary - Pointer to the pointer of properitary values. + */ +void Analog_Value_Property_Lists( + const int **pRequired, const int **pOptional, const int **pProprietary) +{ + if (pRequired) { + *pRequired = Analog_Value_Properties_Required; + } + if (pOptional) { + *pOptional = Analog_Value_Properties_Optional; + } + if (pProprietary) { + *pProprietary = Analog_Value_Properties_Proprietary; + } + + return; +} + +/** + * Initialize the analog values. + */ +void Analog_Value_Init(void) +{ + unsigned i; +#if defined(INTRINSIC_REPORTING) + unsigned j; +#endif + + for (i = 0; i < MAX_ANALOG_VALUES; i++) { + memset(&AV_Descr[i], 0x00, sizeof(ANALOG_VALUE_DESCR)); + AV_Descr[i].Present_Value = 0.0; + AV_Descr[i].Units = UNITS_NO_UNITS; + AV_Descr[i].Prior_Value = 0.0f; + AV_Descr[i].COV_Increment = 1.0f; + AV_Descr[i].Changed = false; +#if defined(INTRINSIC_REPORTING) + AV_Descr[i].Event_State = EVENT_STATE_NORMAL; + /* notification class not connected */ + AV_Descr[i].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(&AV_Descr[i].Event_Time_Stamps[j]); + AV_Descr[i].Acked_Transitions[j].bIsAcked = true; + } + + /* Set handler for GetEventInformation function */ + handler_get_event_information_set( + OBJECT_ANALOG_VALUE, 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 + } +} + +/** + * We simply have 0-n object instances. Yours might be + * more complex, and then you need validate that the + * given instance exists. + * + * @param object_instance Object instance + * + * @return true/false + */ +bool Analog_Value_Valid_Instance(uint32_t object_instance) +{ + if (object_instance < MAX_ANALOG_VALUES) { + return true; + } + + return false; +} + +/** + * Return the count of analog values. + * + * @return Count of analog values. + */ +unsigned Analog_Value_Count(void) +{ + return MAX_ANALOG_VALUES; +} + +/** + * We simply have 0-n object instances. Yours might be + * more complex, and then you need to return the instance + * that correlates to the correct index. + * + * @param index Index + * + * @return Object instance + */ +uint32_t Analog_Value_Index_To_Instance(unsigned index) +{ + return index; +} + +/** + * We simply have 0-n object instances. Yours might be + * more complex, and then you need to return the index + * that correlates to the correct instance number + * + * @param object_instance Object instance + * + * @return Index in the object table. + */ +unsigned Analog_Value_Instance_To_Index(uint32_t object_instance) +{ + unsigned index = MAX_ANALOG_VALUES; + + if (object_instance < MAX_ANALOG_VALUES) { + index = object_instance; + } + + return index; +} + +/** + * This function is used to detect a value change, + * using the new value compared against the prior + * value, using a delta as threshold. + * + * This method will update the COV-changed attribute. + * + * @param index Object index + * @param value Given present value. + */ +static void Analog_Value_COV_Detect(unsigned int index, float value) +{ + float prior_value = 0.0; + float cov_increment = 0.0; + float cov_delta = 0.0; + + if (index < MAX_ANALOG_VALUES) { + prior_value = AV_Descr[index].Prior_Value; + cov_increment = AV_Descr[index].COV_Increment; + if (prior_value > value) { + cov_delta = prior_value - value; + } else { + cov_delta = value - prior_value; + } + if (cov_delta >= cov_increment) { + AV_Descr[index].Changed = true; + AV_Descr[index].Prior_Value = value; + } + } +} + +/** + * For a given object instance-number, sets the present-value at a given + * priority 1..16. + * + * @param object_instance - object-instance number of the object + * @param value - floating point analog value + * @param priority - priority 1..16 + * + * @return true if values are within range and present-value is set. + */ +bool Analog_Value_Present_Value_Set( + uint32_t object_instance, float value, uint8_t priority) +{ + unsigned index = 0; + bool status = false; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + Analog_Value_COV_Detect(index, value); + AV_Descr[index].Present_Value = value; + status = true; + } + return status; +} + +/** + * For a given object instance-number, return the present value. + * + * @param object_instance - object-instance number of the object + * + * @return Present value + */ +float Analog_Value_Present_Value(uint32_t object_instance) +{ + float value = 0; + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = AV_Descr[index].Present_Value; + } + + return value; +} + +/** + * For a given object instance-number, return the name. + * + * Note: the object name must be unique within this device + * + * @param object_instance - object-instance number of the object + * @param object_name - object name/string pointer + * + * @return true/false + */ +bool Analog_Value_Object_Name( + uint32_t object_instance, BACNET_CHARACTER_STRING *object_name) +{ + static char text_string[32] = ""; /* okay for single thread */ + bool status = false; + + if (object_instance < MAX_ANALOG_VALUES) { + sprintf( + text_string, "ANALOG VALUE %lu", (unsigned long)object_instance); + status = characterstring_init_ansi(object_name, text_string); + } + + return status; +} + +/** + * 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 Analog_Value_Event_State(uint32_t object_instance) +{ + unsigned state = EVENT_STATE_NORMAL; + +#if defined(INTRINSIC_REPORTING) + unsigned index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + state = AV_Descr[index].Event_State; + } +#endif + + return state; +} + +/** + * For a given object instance-number, determines if the COV flag + * has been triggered. + * + * @param object_instance - object-instance number of the object + * + * @return true if the COV flag is set + */ +bool Analog_Value_Change_Of_Value(uint32_t object_instance) +{ + unsigned index = 0; + bool changed = false; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + changed = AV_Descr[index].Changed; + } + + return changed; +} + +/** + * For a given object instance-number, clears the COV flag + * + * @param object_instance - object-instance number of the object + */ +void Analog_Value_Change_Of_Value_Clear(uint32_t object_instance) +{ + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + AV_Descr[index].Changed = false; + } +} + +/** + * For a given object instance-number, loads the value_list with the COV data. + * + * @param object_instance - object-instance number of the object + * @param value_list - list of COV data + * + * @return true if the value list is encoded + */ +bool Analog_Value_Encode_Value_List( + uint32_t object_instance, BACNET_PROPERTY_VALUE *value_list) +{ + bool status = false; + + if (value_list) { + value_list->propertyIdentifier = PROP_PRESENT_VALUE; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_REAL; + value_list->value.type.Real = + Analog_Value_Present_Value(object_instance); + value_list->value.next = NULL; + value_list->priority = BACNET_NO_PRIORITY; + value_list = value_list->next; + } + if (value_list) { + value_list->propertyIdentifier = PROP_STATUS_FLAGS; + value_list->propertyArrayIndex = BACNET_ARRAY_ALL; + value_list->value.context_specific = false; + value_list->value.tag = BACNET_APPLICATION_TAG_BIT_STRING; + bitstring_init(&value_list->value.type.Bit_String); + if (Analog_Value_Event_State(object_instance) == EVENT_STATE_NORMAL) { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_IN_ALARM, false); + } else { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_IN_ALARM, true); + } + bitstring_set_bit( + &value_list->value.type.Bit_String, STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &value_list->value.type.Bit_String, STATUS_FLAG_OVERRIDDEN, false); + if (Analog_Value_Out_Of_Service(object_instance)) { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, true); + } else { + bitstring_set_bit(&value_list->value.type.Bit_String, + STATUS_FLAG_OUT_OF_SERVICE, false); + } + value_list->value.next = NULL; + value_list->priority = BACNET_NO_PRIORITY; + value_list->next = NULL; + status = true; + } + + return status; +} + +float Analog_Value_COV_Increment(uint32_t object_instance) +{ + unsigned index = 0; + float value = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = AV_Descr[index].COV_Increment; + } + + return value; +} + +void Analog_Value_COV_Increment_Set(uint32_t object_instance, float value) +{ + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + AV_Descr[index].COV_Increment = value; + Analog_Value_COV_Detect(index, AV_Descr[index].Present_Value); + } +} + +bool Analog_Value_Out_Of_Service(uint32_t object_instance) +{ + unsigned index = 0; + bool value = false; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + value = AV_Descr[index].Out_Of_Service; + } + + return value; +} + +void Analog_Value_Out_Of_Service_Set(uint32_t object_instance, bool value) +{ + unsigned index = 0; + + index = Analog_Value_Instance_To_Index(object_instance); + if (index < MAX_ANALOG_VALUES) { + if (AV_Descr[index].Out_Of_Service != value) { + AV_Descr[index].Changed = true; + } + AV_Descr[index].Out_Of_Service = value; + } +} + +/** + * Return the requested property of the analog value. + * + * @param rpdata Property requested, see for BACNET_READ_PROPERTY_DATA details. + * + * @return apdu len, or BACNET_STATUS_ERROR on error + */ +int Analog_Value_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) +{ + int apdu_len = 0; /* return value */ + BACNET_BIT_STRING bit_string; + BACNET_CHARACTER_STRING char_string; + float real_value = (float)1.414; + unsigned object_index = 0; + bool state = false; + uint8_t *apdu = NULL; + ANALOG_VALUE_DESCR *CurrentAV; +#if defined(INTRINSIC_REPORTING) + int len = 0; + unsigned i = 0; +#endif + + /* Valid data? */ + if (rpdata == NULL) { + return 0; + } + if ((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) { + rpdata->error_class = ERROR_CLASS_OBJECT; + rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return BACNET_STATUS_ERROR; + } + + CurrentAV = &AV_Descr[object_index]; + + 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: + if (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, + Analog_Value_Event_State(rpdata->object_instance) != + EVENT_STATE_NORMAL); + bitstring_set_bit(&bit_string, STATUS_FLAG_FAULT, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&bit_string, STATUS_FLAG_OUT_OF_SERVICE, + CurrentAV->Out_Of_Service); + + apdu_len = encode_application_bitstring(&apdu[0], &bit_string); + break; + + case PROP_EVENT_STATE: +#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: + state = CurrentAV->Out_Of_Service; + apdu_len = encode_application_boolean(&apdu[0], state); + break; + + case PROP_UNITS: + apdu_len = + encode_application_enumerated(&apdu[0], CurrentAV->Units); + break; + + case PROP_COV_INCREMENT: + apdu_len = + encode_application_real(&apdu[0], CurrentAV->COV_Increment); + 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, + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT, + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL, + CurrentAV->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], 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], MAX_BACNET_EVENT_TRANSITION); + /* if no index was specified, then try to encode the entire list */ + /* into one packet. */ + else if (rpdata->array_index == BACNET_ARRAY_ALL) { + for (i = 0; i < MAX_BACNET_EVENT_TRANSITION; i++) { + len = encode_opening_tag( + &apdu[apdu_len], TIME_STAMP_DATETIME); + len += encode_application_date(&apdu[apdu_len + len], + &CurrentAV->Event_Time_Stamps[i].date); + len += encode_application_time(&apdu[apdu_len + len], + &CurrentAV->Event_Time_Stamps[i].time); + len += encode_closing_tag( + &apdu[apdu_len + len], TIME_STAMP_DATETIME); + + /* add it if we have room */ + if ((apdu_len + len) < MAX_APDU) + apdu_len += len; + else { + rpdata->error_code = + ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; + apdu_len = BACNET_STATUS_ABORT; + break; + } + } + } else if (rpdata->array_index <= MAX_BACNET_EVENT_TRANSITION) { + apdu_len = + encode_opening_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + apdu_len += encode_application_date(&apdu[apdu_len], + &CurrentAV->Event_Time_Stamps[rpdata->array_index].date); + apdu_len += encode_application_time(&apdu[apdu_len], + &CurrentAV->Event_Time_Stamps[rpdata->array_index].time); + apdu_len += + encode_closing_tag(&apdu[apdu_len], TIME_STAMP_DATETIME); + } 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; + apdu_len = BACNET_STATUS_ERROR; + break; + } + /* only array properties can have array options */ + if ((apdu_len >= 0) && (rpdata->object_property != PROP_PRIORITY_ARRAY) && + (rpdata->object_property != PROP_EVENT_TIME_STAMPS) && + (rpdata->array_index != BACNET_ARRAY_ALL)) { + rpdata->error_class = ERROR_CLASS_PROPERTY; + rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + apdu_len = BACNET_STATUS_ERROR; + } + + return apdu_len; +} + +/** + * Set the requested property of the analog value. + * + * @param wp_data Property requested, see for BACNET_WRITE_PROPERTY_DATA + * details. + * + * @return true if successful + */ +bool Analog_Value_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + bool status = false; /* return value */ + unsigned int object_index = 0; + int len = 0; + BACNET_APPLICATION_DATA_VALUE value; + ANALOG_VALUE_DESCR *CurrentAV; + + /* Valid data? */ + if (wp_data == NULL) { + return false; + } + if (wp_data->application_data_len == 0) { + return false; + } + + /* decode the some of the request */ + len = bacapp_decode_application_data( + wp_data->application_data, wp_data->application_data_len, &value); + /* FIXME: len < application_data_len: more data? */ + if (len < 0) { + /* error while decoding - a value larger than we can handle */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + return false; + } + if ((wp_data->object_property != PROP_PRIORITY_ARRAY) && + (wp_data->object_property != PROP_EVENT_TIME_STAMPS) && + (wp_data->array_index != BACNET_ARRAY_ALL)) { + /* only array properties can have array options */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + return false; + } + + /* Valid object? */ + object_index = Analog_Value_Instance_To_Index(wp_data->object_instance); + if (object_index >= MAX_ANALOG_VALUES) { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + return false; + } + + CurrentAV = &AV_Descr[object_index]; + + switch (wp_data->object_property) { + case PROP_PRESENT_VALUE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + if (Analog_Value_Present_Value_Set(wp_data->object_instance, + value.type.Real, wp_data->priority)) { + status = true; + } else if (wp_data->priority == 6) { + /* Command priority 6 is reserved for use by Minimum On/Off + algorithm and may not be used for other purposes in any + object. */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + break; + + case PROP_OUT_OF_SERVICE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BOOLEAN); + if (status) { + CurrentAV->Out_Of_Service = value.type.Boolean; + } + break; + + case PROP_UNITS: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + CurrentAV->Units = value.type.Enumerated; + } + break; + + case PROP_COV_INCREMENT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + if (value.type.Real >= 0.0) { + Analog_Value_COV_Increment_Set( + wp_data->object_instance, value.type.Real); + } else { + status = false; + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } + break; + +#if defined(INTRINSIC_REPORTING) + case PROP_TIME_DELAY: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + CurrentAV->Time_Delay = value.type.Unsigned_Int; + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + } + break; + + case PROP_NOTIFICATION_CLASS: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_UNSIGNED_INT); + if (status) { + CurrentAV->Notification_Class = value.type.Unsigned_Int; + } + break; + + case PROP_HIGH_LIMIT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAV->High_Limit = value.type.Real; + } + break; + + case PROP_LOW_LIMIT: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAV->Low_Limit = value.type.Real; + } + break; + + case PROP_DEADBAND: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_REAL); + if (status) { + CurrentAV->Deadband = value.type.Real; + } + break; + + case PROP_LIMIT_ENABLE: + status = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BIT_STRING); + 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 = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_BIT_STRING); + 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 = write_property_type_valid(wp_data, &value, + BACNET_APPLICATION_TAG_ENUMERATED); + if (status) { + switch ((BACNET_NOTIFY_TYPE)value.type.Enumerated) { + case NOTIFY_EVENT: + CurrentAV->Notify_Type = 1; + break; + case NOTIFY_ALARM: + CurrentAV->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 + case PROP_OBJECT_IDENTIFIER: + case PROP_OBJECT_NAME: + case PROP_OBJECT_TYPE: + case PROP_STATUS_FLAGS: + case PROP_EVENT_STATE: + case PROP_DESCRIPTION: +#if defined(INTRINSIC_REPORTING) + case PROP_ACKED_TRANSITIONS: + case PROP_EVENT_TIME_STAMPS: +#endif + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + break; + case PROP_RELINQUISH_DEFAULT: + case PROP_PRIORITY_ARRAY: + default: + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + break; + } + + return status; +} + +void Analog_Value_Intrinsic_Reporting(uint32_t object_instance) +{ +#if defined(INTRINSIC_REPORTING) + BACNET_EVENT_NOTIFICATION_DATA event_data; + BACNET_CHARACTER_STRING msgText; + ANALOG_VALUE_DESCR *CurrentAV; + unsigned int object_index; + uint8_t FromState = 0; + uint8_t ToState; + float ExceededLimit = 0.0f; + float PresentVal = 0.0f; + bool SendNotify = false; + + 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 */ + + if (CurrentAV->Ack_notify_data.bSendAckNotify) { + /* clean bSendAckNotify flag */ + CurrentAV->Ack_notify_data.bSendAckNotify = false; + /* copy toState */ + ToState = CurrentAV->Ack_notify_data.EventState; + +#if PRINT_ENABLED + fprintf(stderr, "Send Acknotification for (%s,%d).\n", + bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance); +#endif /* PRINT_ENABLED */ + + characterstring_init_ansi(&msgText, "AckNotification"); + + /* Notify Type */ + event_data.notifyType = NOTIFY_ACK_NOTIFICATION; + + /* Send EventNotification. */ + SendNotify = true; + } else { + /* 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) && + ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_HIGH_LIMIT; + else + CurrentAV->Remaining_Time_Delay--; + 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) && + ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) == + EVENT_ENABLE_TO_OFFNORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_LOW_LIMIT; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + 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) && + ((CurrentAV->Limit_Enable & EVENT_HIGH_LIMIT_ENABLE) == + EVENT_HIGH_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_NORMAL; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + 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) && + ((CurrentAV->Limit_Enable & EVENT_LOW_LIMIT_ENABLE) == + EVENT_LOW_LIMIT_ENABLE) && + ((CurrentAV->Event_Enable & EVENT_ENABLE_TO_NORMAL) == + EVENT_ENABLE_TO_NORMAL)) { + if (!CurrentAV->Remaining_Time_Delay) + CurrentAV->Event_State = EVENT_STATE_NORMAL; + else + CurrentAV->Remaining_Time_Delay--; + break; + } + /* value of the object is still in the same event state */ + CurrentAV->Remaining_Time_Delay = CurrentAV->Time_Delay; + 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) */ + +#if PRINT_ENABLED + fprintf(stderr, "Event_State for (%s,%d) goes from %s to %s.\n", + bactext_object_type_name(OBJECT_ANALOG_VALUE), object_instance, + bactext_event_state_name(FromState), + bactext_event_state_name(ToState)); +#endif /* PRINT_ENABLED */ + + /* Notify Type */ + event_data.notifyType = CurrentAV->Notify_Type; + + /* Send EventNotification. */ + SendNotify = true; + } + } + + if (SendNotify) { + /* 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); + + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* fill Event_Time_Stamps */ + switch (ToState) { + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_OFFNORMAL] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_FAULT] = + event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAV->Event_Time_Stamps[TRANSITION_TO_NORMAL] = + event_data.timeStamp.value.dateTime; + break; + } + } + + /* 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 */ + /* filled before */ + + /* From State */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) + event_data.fromState = FromState; + + /* To State */ + event_data.toState = CurrentAV->Event_State; + + /* Event Values */ + if (event_data.notifyType != NOTIFY_ACK_NOTIFICATION) { + /* Value that exceeded a limit. */ + event_data.notificationParams.outOfRange.exceedingValue = + PresentVal; + /* Status_Flags of the referenced object. */ + bitstring_init( + &event_data.notificationParams.outOfRange.statusFlags); + bitstring_set_bit( + &event_data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, + CurrentAV->Event_State != EVENT_STATE_NORMAL); + 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); + /* Deadband used for limit checking. */ + event_data.notificationParams.outOfRange.deadband = + CurrentAV->Deadband; + /* Limit that was exceeded. */ + event_data.notificationParams.outOfRange.exceededLimit = + ExceededLimit; + } + + /* add data from notification class */ + Notification_Class_common_reporting_function(&event_data); + + /* Ack required */ + if ((event_data.notifyType != NOTIFY_ACK_NOTIFICATION) && + (event_data.ackRequired == true)) { + switch (event_data.toState) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked = false; + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_FAULT: + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + false; + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + case EVENT_STATE_NORMAL: + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL] + .bIsAcked = false; + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp = event_data.timeStamp.value.dateTime; + break; + + default: /* shouldn't happen */ + break; + } + } + } +#endif /* defined(INTRINSIC_REPORTING) */ +} + +#if defined(INTRINSIC_REPORTING) +int Analog_Value_Event_Information( + unsigned index, BACNET_GET_EVENT_INFORMATION_DATA *getevent_data) +{ + bool IsNotAckedTransitions; + bool IsActiveEvent; + int i; + + /* check index */ + if (index < MAX_ANALOG_VALUES) { + /* Event_State not equal to NORMAL */ + IsActiveEvent = (AV_Descr[index].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 = + (AV_Descr[index] + .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked == false) | + (AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked == + false) | + (AV_Descr[index].Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked == + false); + } else + return -1; /* end of list */ + + if ((IsActiveEvent) || (IsNotAckedTransitions)) { + /* Object Identifier */ + getevent_data->objectIdentifier.type = OBJECT_ANALOG_VALUE; + getevent_data->objectIdentifier.instance = + Analog_Value_Index_To_Instance(index); + /* Event State */ + getevent_data->eventState = AV_Descr[index].Event_State; + /* Acknowledged Transitions */ + bitstring_init(&getevent_data->acknowledgedTransitions); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_OFFNORMAL, + AV_Descr[index] + .Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_FAULT, + AV_Descr[index].Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked); + bitstring_set_bit(&getevent_data->acknowledgedTransitions, + TRANSITION_TO_NORMAL, + AV_Descr[index].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 = + AV_Descr[index].Event_Time_Stamps[i]; + } + /* Notify Type */ + getevent_data->notifyType = AV_Descr[index].Notify_Type; + /* Event Enable */ + bitstring_init(&getevent_data->eventEnable); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_OFFNORMAL, + (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_OFFNORMAL) ? true + : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_FAULT, + (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_FAULT) ? true + : false); + bitstring_set_bit(&getevent_data->eventEnable, TRANSITION_TO_NORMAL, + (AV_Descr[index].Event_Enable & EVENT_ENABLE_TO_NORMAL) ? true + : false); + /* Event Priorities */ + Notification_Class_Get_Priorities( + AV_Descr[index].Notification_Class, getevent_data->eventPriorities); + + return 1; /* active event */ + } else + return 0; /* no active event at this index */ +} + +int Analog_Value_Alarm_Ack( + BACNET_ALARM_ACK_DATA *alarmack_data, BACNET_ERROR_CODE *error_code) +{ + ANALOG_VALUE_DESCR *CurrentAV; + unsigned int object_index; + + object_index = Analog_Value_Instance_To_Index( + alarmack_data->eventObjectIdentifier.instance); + + if (object_index < MAX_ANALOG_VALUES) + CurrentAV = &AV_Descr[object_index]; + else { + *error_code = ERROR_CODE_UNKNOWN_OBJECT; + return -1; + } + + switch (alarmack_data->eventStateAcked) { + case EVENT_STATE_OFFNORMAL: + case EVENT_STATE_HIGH_LIMIT: + case EVENT_STATE_LOW_LIMIT: + if (CurrentAV->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( + &CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_OFFNORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAV->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_FAULT: + if (CurrentAV->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( + &CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAV->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + case EVENT_STATE_NORMAL: + if (CurrentAV->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( + &CurrentAV->Acked_Transitions[TRANSITION_TO_FAULT] + .Time_Stamp, + &alarmack_data->eventTimeStamp.value.dateTime) > 0) { + *error_code = ERROR_CODE_INVALID_TIME_STAMP; + return -1; + } + + /* Clean transitions flag. */ + CurrentAV->Acked_Transitions[TRANSITION_TO_NORMAL].bIsAcked = + true; + } else if (alarmack_data->eventStateAcked == + CurrentAV->Event_State) { + /* Send ack notification */ + } else { + *error_code = ERROR_CODE_INVALID_EVENT_STATE; + return -1; + } + break; + + default: + return -2; + } + + /* Need to send AckNotification. */ + CurrentAV->Ack_notify_data.bSendAckNotify = true; + CurrentAV->Ack_notify_data.EventState = alarmack_data->eventStateAcked; + + /* 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/ports/zephyr/event.c b/ports/zephyr/event.c new file mode 100644 index 00000000..170a9bdb --- /dev/null +++ b/ports/zephyr/event.c @@ -0,0 +1,2080 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2008 John Minack + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + The Free Software Foundation, Inc. + 59 Temple Place - Suite 330 + Boston, MA 02111-1307, USA. + + As a special exception, if other files instantiate templates or + use macros or inline functions from this file, or you compile + this file and link it with other works to produce a work based + on this file, this file does not by itself cause the resulting + work to be covered by the GNU General Public License. However + the source code for this file must still be made available in + accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work + based on this file might be covered by the GNU General Public + License. + ------------------------------------------- +####COPYRIGHTEND####*/ +#include +#include "bacnet/event.h" +#include "bacnet/bacdcode.h" +#include "bacnet/npdu.h" +#include "bacnet/timestamp.h" +#include "bacnet/authentication_factor.h" + +/** @file event.c Encode/Decode Event Notifications */ + +int uevent_notify_encode_apdu( + uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST; + apdu[1] = SERVICE_UNCONFIRMED_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 2; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if (len > 0) { + apdu_len += len; + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +int cevent_notify_encode_apdu( + uint8_t *apdu, uint8_t invoke_id, BACNET_EVENT_NOTIFICATION_DATA *data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; + apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); + apdu[2] = invoke_id; + apdu[3] = SERVICE_CONFIRMED_EVENT_NOTIFICATION; /* service choice */ + apdu_len = 4; + + len += event_notify_encode_service_request(&apdu[apdu_len], data); + + if (len > 0) { + apdu_len += len; + } else { + apdu_len = 0; + } + } + + return apdu_len; +} + +int event_notify_encode_service_request( + uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (apdu) { + /* tag 0 - processIdentifier */ + len = encode_context_unsigned( + &apdu[apdu_len], 0, data->processIdentifier); + apdu_len += len; + /* tag 1 - initiatingObjectIdentifier */ + len = encode_context_object_id(&apdu[apdu_len], 1, + data->initiatingObjectIdentifier.type, + data->initiatingObjectIdentifier.instance); + apdu_len += len; + + /* tag 2 - eventObjectIdentifier */ + len = encode_context_object_id(&apdu[apdu_len], 2, + data->eventObjectIdentifier.type, + data->eventObjectIdentifier.instance); + apdu_len += len; + + /* tag 3 - timeStamp */ + + len = bacapp_encode_context_timestamp( + &apdu[apdu_len], 3, &data->timeStamp); + apdu_len += len; + + /* tag 4 - noticicationClass */ + + len = encode_context_unsigned( + &apdu[apdu_len], 4, data->notificationClass); + apdu_len += len; + + /* tag 5 - priority */ + + len = encode_context_unsigned(&apdu[apdu_len], 5, data->priority); + apdu_len += len; + + /* tag 6 - eventType */ + len = encode_context_enumerated(&apdu[apdu_len], 6, data->eventType); + apdu_len += len; + + /* tag 7 - messageText */ + if (data->messageText) { + len = encode_context_character_string( + &apdu[apdu_len], 7, data->messageText); + apdu_len += len; + } + /* tag 8 - notifyType */ + len = encode_context_enumerated(&apdu[apdu_len], 8, data->notifyType); + apdu_len += len; + + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + + len = encode_context_boolean( + &apdu[apdu_len], 9, data->ackRequired); + apdu_len += len; + + /* tag 10 - fromState */ + len = encode_context_enumerated( + &apdu[apdu_len], 10, data->fromState); + apdu_len += len; + break; + + default: + break; + } + + /* tag 11 - toState */ + len = encode_context_enumerated(&apdu[apdu_len], 11, data->toState); + apdu_len += len; + + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 12 - event values */ + len = encode_opening_tag(&apdu[apdu_len], 12); + apdu_len += len; + + switch (data->eventType) { + case EVENT_CHANGE_OF_BITSTRING: + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 0, + &data->notificationParams.changeOfBitstring + .referencedBitString); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfBitstring + .statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_STATE: + len = encode_opening_tag(&apdu[apdu_len], 1); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = bacapp_encode_property_state(&apdu[apdu_len], + &data->notificationParams.changeOfState.newState); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfState + .statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 1); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_VALUE: + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + switch (data->notificationParams.changeOfValue.tag) { + case CHANGE_OF_VALUE_REAL: + len = encode_context_real(&apdu[apdu_len], 1, + data->notificationParams.changeOfValue + .newValue.changeValue); + apdu_len += len; + break; + + case CHANGE_OF_VALUE_BITS: + len = + encode_context_bitstring(&apdu[apdu_len], 0, + &data->notificationParams.changeOfValue + .newValue.changedBits); + apdu_len += len; + break; + + default: + return 0; + } + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.changeOfValue + .statusFlags); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + break; + + case EVENT_COMMAND_FAILURE: + + len = encode_opening_tag(&apdu[apdu_len], 3); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 0); + apdu_len += len; + + switch (data->notificationParams.commandFailure.tag) { + case COMMAND_FAILURE_BINARY_PV: + len = + encode_application_enumerated(&apdu + [apdu_len], + data->notificationParams.commandFailure. + commandValue.binaryValue); + apdu_len += len; + break; + + case COMMAND_FAILURE_UNSIGNED: + len = + encode_application_unsigned(&apdu + [apdu_len], + data->notificationParams.commandFailure. + commandValue.unsignedValue); + apdu_len += len; + break; + + default: + return 0; + } + + len = encode_closing_tag(&apdu[apdu_len], 0); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.commandFailure. + statusFlags); + apdu_len += len; + + len = encode_opening_tag(&apdu[apdu_len], 2); + apdu_len += len; + + switch (data->notificationParams.commandFailure.tag) { + case COMMAND_FAILURE_BINARY_PV: + len = + encode_application_enumerated(&apdu + [apdu_len], + data->notificationParams.commandFailure. + feedbackValue.binaryValue); + apdu_len += len; + break; + + case COMMAND_FAILURE_UNSIGNED: + len = + encode_application_unsigned(&apdu + [apdu_len], + data->notificationParams.commandFailure. + feedbackValue.unsignedValue); + apdu_len += len; + break; + + default: + return 0; + } + + len = encode_closing_tag(&apdu[apdu_len], 2); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 3); + apdu_len += len; + break; + + case EVENT_FLOATING_LIMIT: + len = encode_opening_tag(&apdu[apdu_len], 4); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 0, + data->notificationParams.floatingLimit + .referenceValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.floatingLimit + .statusFlags); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 2, + data->notificationParams.floatingLimit + .setPointValue); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.floatingLimit.errorLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 4); + apdu_len += len; + break; + + case EVENT_OUT_OF_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 5); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 0, + data->notificationParams.outOfRange.exceedingValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.outOfRange.statusFlags); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 2, + data->notificationParams.outOfRange.deadband); + apdu_len += len; + + len = encode_context_real(&apdu[apdu_len], 3, + data->notificationParams.outOfRange.exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 5); + apdu_len += len; + break; + + case EVENT_CHANGE_OF_LIFE_SAFETY: + len = encode_opening_tag(&apdu[apdu_len], 8); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 0, + data->notificationParams.changeOfLifeSafety + .newState); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 1, + data->notificationParams.changeOfLifeSafety + .newMode); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 2, + &data->notificationParams.changeOfLifeSafety + .statusFlags); + apdu_len += len; + + len = encode_context_enumerated(&apdu[apdu_len], 3, + data->notificationParams.changeOfLifeSafety + .operationExpected); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 8); + apdu_len += len; + break; + + case EVENT_BUFFER_READY: + len = encode_opening_tag(&apdu[apdu_len], 10); + apdu_len += len; + + len = bacapp_encode_context_device_obj_property_ref( + &apdu[apdu_len], 0, + &data->notificationParams.bufferReady + .bufferProperty); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 1, + data->notificationParams.bufferReady + .previousNotification); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams.bufferReady + .currentNotification); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 10); + apdu_len += len; + break; + case EVENT_UNSIGNED_RANGE: + len = encode_opening_tag(&apdu[apdu_len], 11); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 0, + data->notificationParams.unsignedRange + .exceedingValue); + apdu_len += len; + + len = encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.unsignedRange + .statusFlags); + apdu_len += len; + + len = encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams.unsignedRange + .exceededLimit); + apdu_len += len; + + len = encode_closing_tag(&apdu[apdu_len], 11); + apdu_len += len; + break; + case EVENT_ACCESS_EVENT: + len = encode_opening_tag(&apdu[apdu_len], 13); + apdu_len += len; + + len = + encode_context_enumerated(&apdu[apdu_len], 0, + data->notificationParams.accessEvent.accessEvent); + apdu_len += len; + + len = + encode_context_bitstring(&apdu[apdu_len], 1, + &data->notificationParams.accessEvent.statusFlags); + apdu_len += len; + + len = + encode_context_unsigned(&apdu[apdu_len], 2, + data->notificationParams. + accessEvent.accessEventTag); + apdu_len += len; + + len = + bacapp_encode_context_timestamp(&apdu[apdu_len], 3, + &data->notificationParams. + accessEvent.accessEventTime); + apdu_len += len; + + len = + bacapp_encode_context_device_obj_ref(&apdu + [apdu_len], 4, + &data->notificationParams. + accessEvent.accessCredential); + apdu_len += len; + + if (data->notificationParams. + accessEvent.authenticationFactor.format_type < + AUTHENTICATION_FACTOR_MAX) { + len = + bacapp_encode_context_authentication_factor + (&apdu[apdu_len], 5, + &data->notificationParams. + accessEvent.authenticationFactor); + apdu_len += len; + } + + len = encode_closing_tag(&apdu[apdu_len], 13); + apdu_len += len; + break; + case EVENT_EXTENDED: + default: + assert(0); + break; + } + len = encode_closing_tag(&apdu[apdu_len], 12); + apdu_len += len; + break; + case NOTIFY_ACK_NOTIFICATION: + /* FIXME: handle this case */ + default: + break; + } + } + return apdu_len; +} + +int event_notify_decode_service_request( + uint8_t *apdu, unsigned apdu_len, BACNET_EVENT_NOTIFICATION_DATA *data) +{ + int len = 0; /* return value */ + int section_length = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + uint32_t enum_value = 0; + uint32_t len_value = 0; + uint8_t tag_number = 0; + + if (apdu_len && data) { + /* tag 0 - processIdentifier */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 0, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->processIdentifier = (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* tag 1 - initiatingObjectIdentifier */ + if ((section_length = decode_context_object_id(&apdu[len], 1, + &data->initiatingObjectIdentifier.type, + &data->initiatingObjectIdentifier.instance)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 2 - eventObjectIdentifier */ + if ((section_length = decode_context_object_id(&apdu[len], 2, + &data->eventObjectIdentifier.type, + &data->eventObjectIdentifier.instance)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 3 - timeStamp */ + if ((section_length = bacapp_decode_context_timestamp( + &apdu[len], 3, &data->timeStamp)) == -1) { + return -1; + } else { + len += section_length; + } + /* tag 4 - noticicationClass */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 4, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->notificationClass = (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* tag 5 - priority */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 5, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT8_MAX) { + data->priority = (uint8_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* tag 6 - eventType */ + if ((section_length = decode_context_enumerated( + &apdu[len], 6, &enum_value)) == -1) { + return -1; + } else { + data->eventType = (BACNET_EVENT_TYPE)enum_value; + len += section_length; + } + /* tag 7 - messageText */ + + if (decode_is_context_tag(&apdu[len], 7)) { + if (data->messageText != NULL) { + if ((section_length = decode_context_character_string( + &apdu[len], 7, data->messageText)) == -1) { + /*FIXME This is an optional parameter */ + return -1; + } else { + len += section_length; + } + } else { + return -1; + } + } else { + if (data->messageText != NULL) { + characterstring_init_ansi(data->messageText, ""); + } + } + + /* tag 8 - notifyType */ + if ((section_length = decode_context_enumerated( + &apdu[len], 8, &enum_value)) == -1) { + return -1; + } else { + data->notifyType = (BACNET_NOTIFY_TYPE)enum_value; + len += section_length; + } + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + /* tag 9 - ackRequired */ + section_length = + decode_context_boolean2(&apdu[len], 9, &data->ackRequired); + if (section_length == BACNET_STATUS_ERROR) { + return -1; + } + len += section_length; + + /* tag 10 - fromState */ + if ((section_length = decode_context_enumerated( + &apdu[len], 10, &enum_value)) == -1) { + return -1; + } else { + data->fromState = (BACNET_EVENT_STATE)enum_value; + len += section_length; + } + break; + /* In cases other than alarm and event + there's no data, so do not return an error + but continue normally */ + case NOTIFY_ACK_NOTIFICATION: + default: + break; + } + /* tag 11 - toState */ + if ((section_length = decode_context_enumerated( + &apdu[len], 11, &enum_value)) == -1) { + return -1; + } else { + data->toState = (BACNET_EVENT_STATE)enum_value; + len += section_length; + } + /* tag 12 - eventValues */ + switch (data->notifyType) { + case NOTIFY_ALARM: + case NOTIFY_EVENT: + if (decode_is_opening_tag_number(&apdu[len], 12)) { + len++; + } else { + return -1; + } + if (decode_is_opening_tag_number( + &apdu[len], (uint8_t)data->eventType)) { + len++; + } else { + return -1; + } + + switch (data->eventType) { + case EVENT_CHANGE_OF_BITSTRING: + if (-1 == + (section_length = decode_context_bitstring( + &apdu[len], 0, + &data->notificationParams.changeOfBitstring + .referencedBitString))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams + .changeOfBitstring.statusFlags))) { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_STATE: + if (-1 == + (section_length = + bacapp_decode_context_property_state( + &apdu[len], 0, + &data->notificationParams.changeOfState + .newState))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.changeOfState + .statusFlags))) { + return -1; + } + len += section_length; + + break; + + case EVENT_CHANGE_OF_VALUE: + if (!decode_is_opening_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + if (decode_is_context_tag( + &apdu[len], CHANGE_OF_VALUE_BITS)) { + if (-1 == + (section_length = decode_context_bitstring( + &apdu[len], 0, + &data->notificationParams.changeOfValue + .newValue.changedBits))) { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = + CHANGE_OF_VALUE_BITS; + } else if (decode_is_context_tag( + &apdu[len], CHANGE_OF_VALUE_REAL)) { + if (-1 == + (section_length = decode_context_real( + &apdu[len], 1, + &data->notificationParams.changeOfValue + .newValue.changeValue))) { + return -1; + } + + len += section_length; + data->notificationParams.changeOfValue.tag = + CHANGE_OF_VALUE_REAL; + } else { + return -1; + } + if (!decode_is_closing_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.changeOfValue + .statusFlags))) { + return -1; + } + len += section_length; + break; + + case EVENT_COMMAND_FAILURE: + if (!decode_is_opening_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + if (-1 == (section_length = + decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value))) { + return -1; + } + len += section_length; + + switch (tag_number) { + case BACNET_APPLICATION_TAG_ENUMERATED: + if (-1 == (section_length = + decode_enumerated(&apdu[len], + len_value, + &data-> + notificationParams.commandFailure. + commandValue.binaryValue))) { + return -1; + } + break; + + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (-1 == (section_length = + decode_unsigned(&apdu[len], len_value, + &data-> + notificationParams.commandFailure. + commandValue.unsignedValue))) { + return -1; + } + break; + + default: + return 0; + } + len += section_length; + + if (!decode_is_closing_tag_number(&apdu[len], 0)) { + return -1; + } + len++; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.commandFailure. + statusFlags))) { + return -1; + } + len += section_length; + + if (!decode_is_opening_tag_number(&apdu[len], 2)) { + return -1; + } + len++; + + if (-1 == (section_length = + decode_tag_number_and_value(&apdu[len], + &tag_number, &len_value))) { + return -1; + } + len += section_length; + + switch (tag_number) { + case BACNET_APPLICATION_TAG_ENUMERATED: + if (-1 == (section_length = + decode_enumerated(&apdu[len], + len_value, + &data-> + notificationParams.commandFailure. + feedbackValue.binaryValue))) { + return -1; + } + break; + + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (-1 == (section_length = + decode_unsigned(&apdu[len], len_value, + &data-> + notificationParams.commandFailure. + feedbackValue.unsignedValue))) { + return -1; + } + break; + + default: + return 0; + } + len += section_length; + + if (!decode_is_closing_tag_number(&apdu[len], 2)) { + return -1; + } + len++; + + break; + + case EVENT_FLOATING_LIMIT: + if (-1 == + (section_length = decode_context_real(&apdu[len], 0, + &data->notificationParams.floatingLimit + .referenceValue))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.floatingLimit + .statusFlags))) { + return -1; + } + len += section_length; + if (-1 == + (section_length = decode_context_real(&apdu[len], 2, + &data->notificationParams.floatingLimit + .setPointValue))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = decode_context_real(&apdu[len], 3, + &data->notificationParams.floatingLimit + .errorLimit))) { + return -1; + } + len += section_length; + break; + + case EVENT_OUT_OF_RANGE: + if (-1 == + (section_length = decode_context_real(&apdu[len], 0, + &data->notificationParams.outOfRange + .exceedingValue))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.outOfRange + .statusFlags))) { + return -1; + } + len += section_length; + if (-1 == + (section_length = decode_context_real(&apdu[len], 2, + &data->notificationParams.outOfRange + .deadband))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = decode_context_real(&apdu[len], 3, + &data->notificationParams.outOfRange + .exceededLimit))) { + return -1; + } + len += section_length; + break; + + case EVENT_CHANGE_OF_LIFE_SAFETY: + if (-1 == + (section_length = decode_context_enumerated( + &apdu[len], 0, &enum_value))) { + return -1; + } + data->notificationParams.changeOfLifeSafety.newState = + (BACNET_LIFE_SAFETY_STATE)enum_value; + len += section_length; + + if (-1 == + (section_length = decode_context_enumerated( + &apdu[len], 1, &enum_value))) { + return -1; + } + data->notificationParams.changeOfLifeSafety.newMode = + (BACNET_LIFE_SAFETY_MODE)enum_value; + len += section_length; + + if (-1 == + (section_length = decode_context_bitstring( + &apdu[len], 2, + &data->notificationParams.changeOfLifeSafety + .statusFlags))) { + return -1; + } + len += section_length; + + if (-1 == + (section_length = decode_context_enumerated( + &apdu[len], 3, &enum_value))) { + return -1; + } + data->notificationParams.changeOfLifeSafety + .operationExpected = + (BACNET_LIFE_SAFETY_OPERATION)enum_value; + len += section_length; + break; + + case EVENT_BUFFER_READY: + /* Tag 0 - bufferProperty */ + if (-1 == + (section_length = + bacapp_decode_context_device_obj_property_ref( + &apdu[len], 0, + &data->notificationParams.bufferReady + .bufferProperty))) { + return -1; + } + len += section_length; + /* Tag 1 - PreviousNotification */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 1, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->notificationParams.bufferReady + .previousNotification = + (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* Tag 2 - currentNotification */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 2, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->notificationParams.bufferReady + .currentNotification = + (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + + case EVENT_UNSIGNED_RANGE: + /* Tag 0 - PreviousNotification */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 0, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->notificationParams.unsignedRange + .exceedingValue = (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* Tag 1 - statusFlags */ + if (-1 == + (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams.unsignedRange + .statusFlags))) { + return -1; + } + len += section_length; + /* Tag 2 - exceededLimit */ + section_length = bacnet_unsigned_context_decode( + &apdu[len], apdu_len - len, 2, &unsigned_value); + if (section_length > 0) { + len += section_length; + if (unsigned_value <= UINT32_MAX) { + data->notificationParams.unsignedRange + .exceededLimit = (uint32_t)unsigned_value; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + + case EVENT_ACCESS_EVENT: + if (-1 == (section_length = + decode_context_enumerated(&apdu[len], 0, + &data->notificationParams. + accessEvent.accessEvent))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_bitstring(&apdu[len], 1, + &data->notificationParams. + accessEvent.statusFlags))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + decode_context_unsigned(&apdu[len], 2, + &data->notificationParams. + accessEvent.accessEventTag))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + bacapp_decode_context_timestamp(&apdu[len], 3, + &data->notificationParams. + accessEvent.accessEventTime))) { + return -1; + } + len += section_length; + + if (-1 == (section_length = + bacapp_decode_context_device_obj_ref(&apdu + [len], 4, + &data->notificationParams. + accessEvent.accessCredential))) { + return -1; + } + len += section_length; + + if (!decode_is_closing_tag(&apdu[len])) { + if (-1 == (section_length = + bacapp_decode_context_authentication_factor + (&apdu[len], 5, + &data->notificationParams. + accessEvent.authenticationFactor))) { + return -1; + } + len += section_length; + } + break; + + default: + return -1; + } + if (decode_is_closing_tag_number( + &apdu[len], (uint8_t)data->eventType)) { + len++; + } else { + return -1; + } + if (decode_is_closing_tag_number(&apdu[len], 12)) { + len++; + } else { + return -1; + } + break; + /* In cases other than alarm and event + there's no data, so do not return an error + but continue normally */ + case NOTIFY_ACK_NOTIFICATION: + default: + break; + } + } + + return len; +} + +#ifdef BAC_TEST + +#include +#include +#include "ctest.h" + +BACNET_EVENT_NOTIFICATION_DATA data; +BACNET_EVENT_NOTIFICATION_DATA data2; + +void testBaseEventState(Test *pTest) +{ + ct_test(pTest, data.processIdentifier == data2.processIdentifier); + ct_test(pTest, + data.initiatingObjectIdentifier.instance == + data2.initiatingObjectIdentifier.instance); + ct_test(pTest, + data.initiatingObjectIdentifier.type == + data2.initiatingObjectIdentifier.type); + ct_test(pTest, + data.eventObjectIdentifier.instance == + data2.eventObjectIdentifier.instance); + ct_test(pTest, + data.eventObjectIdentifier.type == data2.eventObjectIdentifier.type); + ct_test(pTest, data.notificationClass == data2.notificationClass); + ct_test(pTest, data.priority == data2.priority); + ct_test(pTest, data.notifyType == data2.notifyType); + ct_test(pTest, data.fromState == data2.fromState); + ct_test(pTest, data.toState == data2.toState); + ct_test(pTest, data.toState == data2.toState); + + if (data.messageText != NULL && data2.messageText != NULL) { + ct_test( + pTest, data.messageText->encoding == data2.messageText->encoding); + ct_test(pTest, data.messageText->length == data2.messageText->length); + ct_test(pTest, + strcmp(data.messageText->value, data2.messageText->value) == 0); + } + + ct_test(pTest, data.timeStamp.tag == data2.timeStamp.tag); + + switch (data.timeStamp.tag) { + case TIME_STAMP_SEQUENCE: + ct_test(pTest, + data.timeStamp.value.sequenceNum == + data2.timeStamp.value.sequenceNum); + break; + + case TIME_STAMP_DATETIME: + ct_test(pTest, + data.timeStamp.value.dateTime.time.hour == + data2.timeStamp.value.dateTime.time.hour); + ct_test(pTest, + data.timeStamp.value.dateTime.time.min == + data2.timeStamp.value.dateTime.time.min); + ct_test(pTest, + data.timeStamp.value.dateTime.time.sec == + data2.timeStamp.value.dateTime.time.sec); + ct_test(pTest, + data.timeStamp.value.dateTime.time.hundredths == + data2.timeStamp.value.dateTime.time.hundredths); + + ct_test(pTest, + data.timeStamp.value.dateTime.date.day == + data2.timeStamp.value.dateTime.date.day); + ct_test(pTest, + data.timeStamp.value.dateTime.date.month == + data2.timeStamp.value.dateTime.date.month); + ct_test(pTest, + data.timeStamp.value.dateTime.date.wday == + data2.timeStamp.value.dateTime.date.wday); + ct_test(pTest, + data.timeStamp.value.dateTime.date.year == + data2.timeStamp.value.dateTime.date.year); + break; + + case TIME_STAMP_TIME: + ct_test(pTest, + data.timeStamp.value.time.hour == + data2.timeStamp.value.time.hour); + ct_test(pTest, + data.timeStamp.value.time.min == + data2.timeStamp.value.time.min); + ct_test(pTest, + data.timeStamp.value.time.sec == + data2.timeStamp.value.time.sec); + ct_test(pTest, + data.timeStamp.value.time.hundredths == + data2.timeStamp.value.time.hundredths); + break; + + default: + ct_fail(pTest, "Unknown type"); + break; + } +} + +void testEventEventState(Test *pTest) +{ + uint8_t buffer[MAX_APDU]; + int inLen; + int outLen; + BACNET_CHARACTER_STRING messageText; + BACNET_CHARACTER_STRING messageText2; + characterstring_init_ansi( + &messageText, "This is a test of the message text\n"); + + data.messageText = &messageText; + data2.messageText = &messageText2; + + data.processIdentifier = 1234; + data.initiatingObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.initiatingObjectIdentifier.instance = 100; + data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.eventObjectIdentifier.instance = 200; + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + data.notificationClass = 50; + data.priority = 50; + data.notifyType = NOTIFY_ALARM; + data.fromState = EVENT_STATE_NORMAL; + data.toState = EVENT_STATE_OFFNORMAL; + + data.eventType = EVENT_CHANGE_OF_STATE; + data.notificationParams.changeOfState.newState.tag = UNITS; + data.notificationParams.changeOfState.newState.state.units = + UNITS_SQUARE_METERS; + + bitstring_init(&data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + inLen = event_notify_encode_service_request(&buffer[0], &data); + + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.changeOfState.newState.tag == + data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, + data.notificationParams.changeOfState.newState.state.units == + data2.notificationParams.changeOfState.newState.state.units); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfState.statusFlags, + &data2.notificationParams.changeOfState.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Same, but timestamp of + */ + data.timeStamp.tag = TIME_STAMP_DATETIME; + data.timeStamp.value.dateTime.time.hour = 1; + data.timeStamp.value.dateTime.time.min = 2; + data.timeStamp.value.dateTime.time.sec = 3; + data.timeStamp.value.dateTime.time.hundredths = 4; + + data.timeStamp.value.dateTime.date.day = 1; + data.timeStamp.value.dateTime.date.month = 1; + data.timeStamp.value.dateTime.date.wday = 1; + data.timeStamp.value.dateTime.date.year = 1945; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + ct_test(pTest, + data.notificationParams.changeOfState.newState.tag == + data2.notificationParams.changeOfState.newState.tag); + ct_test(pTest, + data.notificationParams.changeOfState.newState.state.units == + data2.notificationParams.changeOfState.newState.state.units); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + + /* + ** Event Type = EVENT_CHANGE_OF_BITSTRING + */ + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + + data.eventType = EVENT_CHANGE_OF_BITSTRING; + + bitstring_init( + &data.notificationParams.changeOfBitstring.referencedBitString); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 0, + true); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 1, + false); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 2, + true); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 2, + false); + + bitstring_init(&data.notificationParams.changeOfBitstring.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same( + &data.notificationParams.changeOfBitstring.referencedBitString, + &data2.notificationParams.changeOfBitstring.referencedBitString)); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfBitstring.statusFlags, + &data2.notificationParams.changeOfBitstring.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - float value + */ + + data.eventType = EVENT_CHANGE_OF_VALUE; + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; + data.notificationParams.changeOfValue.newValue.changeValue = 1.23f; + + bitstring_init(&data.notificationParams.changeOfValue.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags)); + + ct_test(pTest, + data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + ct_test(pTest, + data.notificationParams.changeOfValue.newValue.changeValue == + data2.notificationParams.changeOfValue.newValue.changeValue); + + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - bitstring value + */ + + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; + + bitstring_init(&data.notificationParams.changeOfValue.newValue.changedBits); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 0, true); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 1, false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 2, false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 3, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags)); + + ct_test(pTest, + data.notificationParams.changeOfValue.tag == + data2.notificationParams.changeOfValue.tag); + + ct_test(pTest, + bitstring_same( + &data.notificationParams.changeOfValue.newValue.changedBits, + &data2.notificationParams.changeOfValue.newValue.changedBits)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_COMMAND_FAILURE + */ + + + /* + ** commandValue = enumerated + */ + data.eventType = EVENT_COMMAND_FAILURE; + data.notificationParams.commandFailure.tag = COMMAND_FAILURE_BINARY_PV; + data.notificationParams.commandFailure.commandValue.binaryValue = + BINARY_INACTIVE; + data.notificationParams.commandFailure.feedbackValue.binaryValue = + BINARY_ACTIVE; + + bitstring_init(&data.notificationParams.commandFailure.statusFlags); + + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.commandFailure.commandValue.binaryValue == + data2.notificationParams.commandFailure.commandValue.binaryValue); + + ct_test(pTest, + data.notificationParams.commandFailure.feedbackValue.binaryValue == + data2.notificationParams.commandFailure.feedbackValue.binaryValue); + + ct_test(pTest, + bitstring_same(&data.notificationParams.commandFailure.statusFlags, + &data2.notificationParams.commandFailure.statusFlags)); + + /* + ** commandValue = unsigned + */ + data.eventType = EVENT_COMMAND_FAILURE; + data.notificationParams.commandFailure.tag = COMMAND_FAILURE_UNSIGNED; + data.notificationParams.commandFailure.commandValue.unsignedValue = 10; + data.notificationParams.commandFailure.feedbackValue.unsignedValue = 2; + + bitstring_init(&data.notificationParams.commandFailure.statusFlags); + + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.commandFailure.commandValue.unsignedValue == + data2.notificationParams.commandFailure.commandValue.unsignedValue); + + ct_test(pTest, + data.notificationParams.commandFailure.feedbackValue.unsignedValue == + data2.notificationParams.commandFailure.feedbackValue.unsignedValue); + + ct_test(pTest, + bitstring_same(&data.notificationParams.commandFailure.statusFlags, + &data2.notificationParams.commandFailure.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_FLOATING_LIMIT + */ + data.eventType = EVENT_FLOATING_LIMIT; + data.notificationParams.floatingLimit.referenceValue = 1.23f; + data.notificationParams.floatingLimit.setPointValue = 2.34f; + data.notificationParams.floatingLimit.errorLimit = 3.45f; + + bitstring_init(&data.notificationParams.floatingLimit.statusFlags); + + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.floatingLimit.referenceValue == + data2.notificationParams.floatingLimit.referenceValue); + + ct_test(pTest, + data.notificationParams.floatingLimit.setPointValue == + data2.notificationParams.floatingLimit.setPointValue); + + ct_test(pTest, + data.notificationParams.floatingLimit.errorLimit == + data2.notificationParams.floatingLimit.errorLimit); + ct_test(pTest, + bitstring_same(&data.notificationParams.floatingLimit.statusFlags, + &data2.notificationParams.floatingLimit.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_OUT_OF_RANGE + */ + data.eventType = EVENT_OUT_OF_RANGE; + data.notificationParams.outOfRange.exceedingValue = 3.45f; + data.notificationParams.outOfRange.deadband = 2.34f; + data.notificationParams.outOfRange.exceededLimit = 1.23f; + + bitstring_init(&data.notificationParams.outOfRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.outOfRange.deadband == + data2.notificationParams.outOfRange.deadband); + + ct_test(pTest, + data.notificationParams.outOfRange.exceededLimit == + data2.notificationParams.outOfRange.exceededLimit); + + ct_test(pTest, + data.notificationParams.outOfRange.exceedingValue == + data2.notificationParams.outOfRange.exceedingValue); + ct_test(pTest, + bitstring_same(&data.notificationParams.outOfRange.statusFlags, + &data2.notificationParams.outOfRange.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_CHANGE_OF_LIFE_SAFETY + */ + data.eventType = EVENT_CHANGE_OF_LIFE_SAFETY; + data.notificationParams.changeOfLifeSafety.newState = + LIFE_SAFETY_STATE_ALARM; + data.notificationParams.changeOfLifeSafety.newMode = LIFE_SAFETY_MODE_ARMED; + data.notificationParams.changeOfLifeSafety.operationExpected = + LIFE_SAFETY_OP_RESET; + + bitstring_init(&data.notificationParams.changeOfLifeSafety.statusFlags); + + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.newMode == + data2.notificationParams.changeOfLifeSafety.newMode); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.newState == + data2.notificationParams.changeOfLifeSafety.newState); + + ct_test(pTest, + data.notificationParams.changeOfLifeSafety.operationExpected == + data2.notificationParams.changeOfLifeSafety.operationExpected); + + ct_test(pTest, + bitstring_same(&data.notificationParams.changeOfLifeSafety.statusFlags, + &data2.notificationParams.changeOfLifeSafety.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_UNSIGNED_RANGE + */ + data.eventType = EVENT_UNSIGNED_RANGE; + data.notificationParams.unsignedRange.exceedingValue = 1234; + data.notificationParams.unsignedRange.exceededLimit = 2345; + + bitstring_init(&data.notificationParams.unsignedRange.statusFlags); + + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.unsignedRange.exceedingValue == + data2.notificationParams.unsignedRange.exceedingValue); + + ct_test(pTest, + data.notificationParams.unsignedRange.exceededLimit == + data2.notificationParams.unsignedRange.exceededLimit); + + ct_test(pTest, + bitstring_same(&data.notificationParams.unsignedRange.statusFlags, + &data2.notificationParams.unsignedRange.statusFlags)); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_BUFFER_READY + */ + data.eventType = EVENT_BUFFER_READY; + data.notificationParams.bufferReady.previousNotification = 1234; + data.notificationParams.bufferReady.currentNotification = 2345; + data.notificationParams.bufferReady.bufferProperty.deviceIdentifier.type = + OBJECT_DEVICE; + data.notificationParams.bufferReady.bufferProperty.deviceIdentifier + .instance = 500; + data.notificationParams.bufferReady.bufferProperty.objectIdentifier.type = + OBJECT_ANALOG_INPUT; + data.notificationParams.bufferReady.bufferProperty.objectIdentifier + .instance = 100; + data.notificationParams.bufferReady.bufferProperty.propertyIdentifier = + PROP_PRESENT_VALUE; + data.notificationParams.bufferReady.bufferProperty.arrayIndex = 0; + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.bufferReady.previousNotification == + data2.notificationParams.bufferReady.previousNotification); + + ct_test(pTest, + data.notificationParams.bufferReady.currentNotification == + data2.notificationParams.bufferReady.currentNotification); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.deviceIdentifier + .type == + data2.notificationParams.bufferReady.bufferProperty.deviceIdentifier + .type); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.deviceIdentifier + .instance == + data2.notificationParams.bufferReady.bufferProperty.deviceIdentifier + .instance); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.objectIdentifier + .instance == + data2.notificationParams.bufferReady.bufferProperty.objectIdentifier + .instance); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.objectIdentifier + .type == + data2.notificationParams.bufferReady.bufferProperty.objectIdentifier + .type); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.propertyIdentifier == + data2.notificationParams.bufferReady.bufferProperty + .propertyIdentifier); + + ct_test(pTest, + data.notificationParams.bufferReady.bufferProperty.arrayIndex == + data2.notificationParams.bufferReady.bufferProperty.arrayIndex); + + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /**********************************************************************************/ + /* + ** Event Type = EVENT_ACCESS_EVENT + */ + + // OPTIONAL authenticationFactor omitted + data.eventType = EVENT_ACCESS_EVENT; + data.notificationParams.accessEvent.accessEvent = + ACCESS_EVENT_LOCKED_BY_HIGHER_AUTHORITY; + data.notificationParams.accessEvent.accessEventTag = 7; + data.notificationParams.accessEvent.accessEventTime.tag = + TIME_STAMP_SEQUENCE; + data.notificationParams.accessEvent.accessEventTime.value.sequenceNum = 17; + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance = 1234; + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type = OBJECT_DEVICE; + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance = 17; + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.type = OBJECT_ACCESS_POINT; + data.notificationParams.accessEvent.authenticationFactor.format_type = AUTHENTICATION_FACTOR_MAX; // omit authenticationFactor + + bitstring_init(&data.notificationParams.accessEvent.statusFlags); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEvent == + data2.notificationParams.accessEvent.accessEvent); + + ct_test(pTest, + bitstring_same(&data.notificationParams.accessEvent.statusFlags, + &data2.notificationParams.accessEvent.statusFlags)); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTag == + data2.notificationParams.accessEvent.accessEventTag); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTime.tag == + data2.notificationParams.accessEvent.accessEventTime.tag); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTime. + value.sequenceNum == + data2.notificationParams.accessEvent.accessEventTime. + value.sequenceNum); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance == + data2.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type == + data2.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance == + data2.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.type == + data2.notificationParams.accessEvent.accessCredential. + objectIdentifier.type); + + // OPTIONAL authenticationFactor included + data.eventType = EVENT_ACCESS_EVENT; + data.notificationParams.accessEvent.accessEvent = + ACCESS_EVENT_LOCKED_BY_HIGHER_AUTHORITY; + data.notificationParams.accessEvent.accessEventTag = 7; + data.notificationParams.accessEvent.accessEventTime.tag = + TIME_STAMP_SEQUENCE; + data.notificationParams.accessEvent.accessEventTime.value.sequenceNum = 17; + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance = 1234; + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type = OBJECT_DEVICE; + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance = 17; + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.type = OBJECT_ACCESS_POINT; + data.notificationParams.accessEvent.authenticationFactor.format_type = + AUTHENTICATION_FACTOR_SIMPLE_NUMBER16; + data.notificationParams.accessEvent.authenticationFactor.format_class = + 215; + uint8_t octetstringValue[2] = { 0x00, 0x10 }; + + octetstring_init(&data.notificationParams.accessEvent. + authenticationFactor.value, octetstringValue, 2); + + bitstring_init(&data.notificationParams.accessEvent.statusFlags); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit(&data.notificationParams.accessEvent.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(buffer, 0, MAX_APDU); + inLen = event_notify_encode_service_request(&buffer[0], &data); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + outLen = event_notify_decode_service_request(&buffer[0], inLen, &data2); + + ct_test(pTest, inLen == outLen); + testBaseEventState(pTest); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEvent == + data2.notificationParams.accessEvent.accessEvent); + + ct_test(pTest, + bitstring_same(&data.notificationParams.accessEvent.statusFlags, + &data2.notificationParams.accessEvent.statusFlags)); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTag == + data2.notificationParams.accessEvent.accessEventTag); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTime.tag == + data2.notificationParams.accessEvent.accessEventTime.tag); + + ct_test(pTest, + data.notificationParams.accessEvent.accessEventTime. + value.sequenceNum == + data2.notificationParams.accessEvent.accessEventTime. + value.sequenceNum); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance == + data2.notificationParams.accessEvent.accessCredential. + deviceIdentifier.instance); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type == + data2.notificationParams.accessEvent.accessCredential. + deviceIdentifier.type); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance == + data2.notificationParams.accessEvent.accessCredential. + objectIdentifier.instance); + + ct_test(pTest, + data.notificationParams.accessEvent.accessCredential. + objectIdentifier.type == + data2.notificationParams.accessEvent.accessCredential. + objectIdentifier.type); + + ct_test(pTest, + data.notificationParams.accessEvent.authenticationFactor.format_type == + data2.notificationParams.accessEvent.authenticationFactor.format_type); + + ct_test(pTest, + data.notificationParams.accessEvent. + authenticationFactor.format_class == + data2.notificationParams.accessEvent. + authenticationFactor.format_class); + + ct_test(pTest, + octetstring_value_same(&data.notificationParams. + accessEvent.authenticationFactor.value, + &data2.notificationParams.accessEvent.authenticationFactor.value)); +} + +#ifdef TEST_EVENT + +int main(void) +{ + Test *pTest; + bool rc; + + pTest = ct_create("BACnet Event", NULL); + /* individual tests */ + rc = ct_addTestFunction(pTest, testEventEventState); + assert(rc); + + ct_setStream(pTest, stdout); + ct_run(pTest); + (void)ct_report(pTest); + ct_destroy(pTest); + + return 0; +} + +#endif /* TEST_EVENT */ +#endif /* BAC_TEST */ diff --git a/west.yml b/west.yml new file mode 100644 index 00000000..e51e8a98 --- /dev/null +++ b/west.yml @@ -0,0 +1,44 @@ +# The default west manifest file for Cutter. +# +# The per-installation west configuration file, .west/config, sets the +# path to the project containing this file in the [manifest] section's +# "path" variable. +# +# This imports the latest upstream Zephyr manifest file. +# +# You can pass your manifest repositories to west init when creating a +# new Zephyr installation. See the west documentation for more +# information. +# +# Beginning with west 0.8, the following command-line options +# can be used to initialize with a repository lacking a west.yml +# file at the top level. +# +# west init +# -m +# --mr +# --mf +# + +manifest: + version: 0.7 # This file uses syntax from west 0.7 + + remotes: + - name: bacnet-stack + url-base: https://github.com/zephyrproject-rtos + - name: zephyrproject-rtos + url-base: https://github.com/bacnet-stack + + defaults: + remote: zephyrproject-rtos + + self: + path: bacnet-stack + + projects: + - name: zephyr + path: zephyr + revision: v2.6.0 + remote: zephyrproject-rtos + west-commands: scripts/west-commands.yml + import: true diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index 6d4e845a..7df5a276 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -254,7 +254,6 @@ set(BACNETSTACK_SRCS ${BACNETSTACK_SRC}/bacnet/datetime.h ${BACNETSTACK_SRC}/bacnet/dcc.c ${BACNETSTACK_SRC}/bacnet/dcc.h - ${BACNETSTACK_SRC}/bacnet/event.c ${BACNETSTACK_SRC}/bacnet/event.h ${BACNETSTACK_SRC}/bacnet/get_alarm_sum.c ${BACNETSTACK_SRC}/bacnet/get_alarm_sum.h @@ -316,9 +315,7 @@ set(BACNETSTACK_BASIC_SRCS ${BACNETSTACK_SRC}/bacnet/basic/object/access_user.c ${BACNETSTACK_SRC}/bacnet/basic/object/access_zone.c ${BACNETSTACK_SRC}/bacnet/basic/object/acc.c - ${BACNETSTACK_SRC}/bacnet/basic/object/ai.c ${BACNETSTACK_SRC}/bacnet/basic/object/ao.c - ${BACNETSTACK_SRC}/bacnet/basic/object/av.c ${BACNETSTACK_SRC}/bacnet/basic/object/bacfile.c # Build error: fseek not supported in Zephyr ${BACNETSTACK_SRC}/bacnet/basic/object/bi.c ${BACNETSTACK_SRC}/bacnet/basic/object/bo.c @@ -334,7 +331,6 @@ set(BACNETSTACK_BASIC_SRCS ${BACNETSTACK_SRC}/bacnet/basic/object/lc.c ${BACNETSTACK_SRC}/bacnet/basic/object/lo.c ${BACNETSTACK_SRC}/bacnet/basic/object/lsp.c - ${BACNETSTACK_SRC}/bacnet/basic/object/Makefile ${BACNETSTACK_SRC}/bacnet/basic/object/ms-input.c ${BACNETSTACK_SRC}/bacnet/basic/object/mso.c ${BACNETSTACK_SRC}/bacnet/basic/object/msv.c @@ -402,6 +398,9 @@ list( $<$:${BACNETSTACK_PORT}/ethernet.c> ${BACNETSTACK_PORT}/main.c ${BACNETSTACK_PORT}/mstimer-init.c + ${BACNETSTACK_PORT}/ai.c + ${BACNETSTACK_PORT}/av.c + ${BACNETSTACK_PORT}/event.c ) diff --git a/zephyr/manifests/zp/zephyr/west.yml b/zephyr/manifests/zp/zephyr/west.yml deleted file mode 100644 index aee478f0..00000000 --- a/zephyr/manifests/zp/zephyr/west.yml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2020 Legrand North America, LLC. -# -# SPDX-License-Identifier: MIT - -# The west manifest file for upstream Zephyr Project's Zephyr repo. -# -# NOTE: Requires west 0.8.0a1 or later (pip3 install west==0.8.0a1) -# -# Usage (from top of workspace): -# west -# --zephyr-base zephyr -# init -# -m https://github.com/bacnet-stack/bacnet-stack -# --mr -# --mf zephyr/manifests/zp/zephyr/west.yml -# --zephyr-base zephyr -# $workspace -# cd $workspace -# west update -# source zephyr/zephyr-env.sh - -manifest: - version: 0.7 - remotes: - - name: zephyrproject-rtos - url-base: https://github.com/zephyrproject-rtos - projects: - - name: zephyr - remote: zephyrproject-rtos - revision: v2.3.0 # Latest integrated release - import: true - path: zephyr - self: - path: bacnet-stack diff --git a/zephyr/samples/hello_bacnet_stack/sample.yaml b/zephyr/samples/hello_bacnet_stack/sample.yaml index 7b6da6de..1827fb56 100644 --- a/zephyr/samples/hello_bacnet_stack/sample.yaml +++ b/zephyr/samples/hello_bacnet_stack/sample.yaml @@ -10,6 +10,6 @@ common: regex: - "Hello BACnet-Stack! (.*)" tests: - sample.basic.hello_bacnet_stack: - platform_whitelist: native_posix + bacnet-stack.sample.basic.hello_bacnet_stack: + platform_allow: native_posix tags: introduction diff --git a/zephyr/samples/profiles/b-ss/sample.yaml b/zephyr/samples/profiles/b-ss/sample.yaml index 08d0d957..16b29fc5 100644 --- a/zephyr/samples/profiles/b-ss/sample.yaml +++ b/zephyr/samples/profiles/b-ss/sample.yaml @@ -2,6 +2,6 @@ sample: name: BACnet Profile B-SS Sample tests: - sample.profile.b-ss: + bacnet-stack.sample.profile.b-ss: tags: bacnet - platform_whitelist: qemu_x86 native_posix native_posix_64 + platform_allow: qemu_x86 native_posix native_posix_64 diff --git a/zephyr/samples/profiles/b-ss/src/main.c b/zephyr/samples/profiles/b-ss/src/main.c index ad83bc8d..c94d7c18 100644 --- a/zephyr/samples/profiles/b-ss/src/main.c +++ b/zephyr/samples/profiles/b-ss/src/main.c @@ -82,14 +82,14 @@ void main(void) datalink_init(NULL); /* configure the timeout values */ - s64_t last_ms = k_uptime_get(); + int64_t last_ms = k_uptime_get(); /* broadcast an I-Am on startup */ Send_I_Am(&Handler_Transmit_Buffer[0]); - s64_t address_binding_tmr = 0; + int64_t address_binding_tmr = 0; #if defined(INTRINSIC_REPORTING) - s64_t recipient_scan_tmr = 0; + int64_t recipient_scan_tmr = 0; #endif #if defined(BACNET_TIME_MASTER) BACNET_DATE_TIME bdatetime = { 0 }; @@ -99,7 +99,7 @@ void main(void) BACNET_ADDRESS src = { 0 }; /* address where message came from */ const unsigned timeout_ms = 1; - s64_t current_ms = k_uptime_get(); + int64_t current_ms = k_uptime_get(); /* returns 0 bytes on timeout */ uint16_t const pdu_len = datalink_receive(&src, &Rx_Buf[0],