From 524fd162f9b69c235551fd91fe522a955241ade8 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 20 May 2025 15:04:14 -0500 Subject: [PATCH] Feature/basic-schedule-object-write-properties (#1000) * Added schedule object WriteProperty handling for effective-period, list-of-object-property-references and exception-schedule properties. --- src/bacnet/bactimevalue.c | 91 +++++ src/bacnet/bactimevalue.h | 7 + src/bacnet/basic/object/schedule.c | 371 ++++++++++++++++++- src/bacnet/basic/object/schedule.h | 34 ++ src/bacnet/dailyschedule.c | 48 +++ src/bacnet/dailyschedule.h | 8 + src/bacnet/special_event.c | 18 + src/bacnet/special_event.h | 4 + test/bacnet/basic/object/schedule/src/main.c | 86 ++++- 9 files changed, 656 insertions(+), 11 deletions(-) diff --git a/src/bacnet/bactimevalue.c b/src/bacnet/bactimevalue.c index 2314da84..22371e35 100644 --- a/src/bacnet/bactimevalue.c +++ b/src/bacnet/bactimevalue.c @@ -369,3 +369,94 @@ int bacnet_time_values_context_encode( return apdu_len; } + +/** + * * @brief Compare two BACnetTimeValue values + * @param a [in] First value to compare + * @param b [in] Second value to compare + * @return true if equal, false if not equal + */ +bool bacnet_time_value_same( + const BACNET_TIME_VALUE *a, const BACNET_TIME_VALUE *b) +{ + if (a == NULL || b == NULL) { + return false; + } + if (a->Time.hour != b->Time.hour) { + return false; + } + if (a->Time.min != b->Time.min) { + return false; + } + if (a->Time.sec != b->Time.sec) { + return false; + } + if (a->Time.hundredths != b->Time.hundredths) { + return false; + } + if (a->Value.tag != b->Value.tag) { + return false; + } + switch (a->Value.tag) { +#if defined(BACAPP_BOOLEAN) + case BACNET_APPLICATION_TAG_BOOLEAN: + if (a->Value.type.Boolean != b->Value.type.Boolean) { + return false; + } + break; +#endif +#if defined(BACAPP_UNSIGNED) + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (a->Value.type.Unsigned_Int != b->Value.type.Unsigned_Int) { + return false; + } + break; +#endif +#if defined(BACAPP_SIGNED) + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (a->Value.type.Signed_Int != b->Value.type.Signed_Int) { + return false; + } + break; +#endif +#if defined(BACAPP_REAL) + case BACNET_APPLICATION_TAG_REAL: + if (islessgreater(a->Value.type.Real, b->Value.type.Real)) { + return false; + } + break; +#endif +#if defined(BACAPP_DOUBLE) + case BACNET_APPLICATION_TAG_DOUBLE: + if (islessgreater(a->Value.type.Double, b->Value.type.Double)) { + return false; + } + break; +#endif +#if defined(BACAPP_ENUMERATED) + case BACNET_APPLICATION_TAG_ENUMERATED: + if (a->Value.type.Enumerated != b->Value.type.Enumerated) { + return false; + } + break; +#endif + default: + break; + } + + return true; +} + +/** + * @brief Copy a BACnetTimeValue value + * @param dest [in] destination to copy to + * @param src [in] source to copy from + */ +void bacnet_time_value_copy( + BACNET_TIME_VALUE *dest, const BACNET_TIME_VALUE *src) +{ + if (dest == NULL || src == NULL) { + return; + } + memcpy(dest, src, sizeof(BACNET_TIME_VALUE)); +} diff --git a/src/bacnet/bactimevalue.h b/src/bacnet/bactimevalue.h index 8211a744..ce76ae1d 100644 --- a/src/bacnet/bactimevalue.h +++ b/src/bacnet/bactimevalue.h @@ -150,6 +150,13 @@ int bacnet_time_values_context_encode( const BACNET_TIME_VALUE *time_values, unsigned int max_time_values); +BACNET_STACK_EXPORT +bool bacnet_time_value_same( + const BACNET_TIME_VALUE *a, const BACNET_TIME_VALUE *b); +BACNET_STACK_EXPORT +void bacnet_time_value_copy( + BACNET_TIME_VALUE *dest, const BACNET_TIME_VALUE *src); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/basic/object/schedule.c b/src/bacnet/basic/object/schedule.c index 00469dbe..92404dcb 100644 --- a/src/bacnet/basic/object/schedule.c +++ b/src/bacnet/basic/object/schedule.c @@ -71,7 +71,7 @@ void Schedule_Property_Lists( * @param object_instance - object-instance number of the object * @return object found in the list, or NULL if not found */ -static SCHEDULE_DESCR *Schedule_Object(uint32_t object_instance) +SCHEDULE_DESCR *Schedule_Object(uint32_t object_instance) { unsigned int object_index; SCHEDULE_DESCR *pObject = NULL; @@ -303,6 +303,52 @@ static int Schedule_Weekly_Schedule_Encode( } #if BACNET_EXCEPTION_SCHEDULE_SIZE +/** + * @brief Get the Exception Schedule for a given object instance + * @param object_instance - object-instance number of the object + * @param array_index - index of the Exception Schedule to get 0 to 6 + * @return pointer to the Exception Schedule BACnetSpecialEvent, + * or NULL if not found + */ +BACNET_SPECIAL_EVENT * +Schedule_Exception_Schedule(uint32_t object_instance, unsigned array_index) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject && (array_index < BACNET_EXCEPTION_SCHEDULE_SIZE)) { + return &pObject->Exception_Schedule[array_index]; + } + + return NULL; +} + +/** + * @brief Set the Exception Schedule for a given object instance + * @param object_instance - object-instance number of the object + * @param array_index - index of the Exception Schedule to set 0 to 6 + * @param value - pointer to the Weekly Schedule BACnetSpecialEvent to set + * @return true if the Exception Schedule BACnetSpecialEvent was set, + * and false if not + */ +bool Schedule_Exception_Schedule_Set( + uint32_t object_instance, + unsigned array_index, + const BACNET_SPECIAL_EVENT *value) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject && (array_index < BACNET_EXCEPTION_SCHEDULE_SIZE)) { + memcpy( + &pObject->Exception_Schedule[array_index], value, + sizeof(BACNET_SPECIAL_EVENT)); + return true; + } + + return false; +} + /** * @brief Encode a BACnetARRAY property element * @param object_instance [in] BACnet network port object instance number @@ -333,6 +379,139 @@ static int Schedule_Exception_Schedule_Encode( } #endif +/** + * @brief Set the Effective Period for a given object instance + * @param object_instance - object-instance number of the object + * @param start_date - start date of the effective period + * @param end_date - end date of the effective period + * @return true if the effective period was set, and false if not + */ +bool Schedule_Effective_Period_Set( + uint32_t object_instance, + const BACNET_DATE *start_date, + const BACNET_DATE *end_date) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + datetime_copy_date(&pObject->Start_Date, start_date); + datetime_copy_date(&pObject->End_Date, end_date); + return true; + } + + return false; +} + +/** + * @brief Get the Effective Period for a given object instance + * @param object_instance - object-instance number of the object + * @param start_date - start date of the effective period + * @param end_date - end date of the effective period + * @return true if the effective period was set, and false if not + */ +bool Schedule_Effective_Period( + uint32_t object_instance, BACNET_DATE *start_date, BACNET_DATE *end_date) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + datetime_copy_date(start_date, &pObject->Start_Date); + datetime_copy_date(end_date, &pObject->End_Date); + return true; + } + + return false; +} + +/** + * @brief Set a member element of a given BACnetLIST object property + * @param pObject - object in which to set the value + * @param index - 0-based array index + * @param pMember - pointer to member value + * @return true if set, false if not set + */ +static bool List_Of_Object_Property_References_Set( + SCHEDULE_DESCR *pObject, + unsigned index, + const BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember) +{ + bool status = false; + if (pObject && (index < BACNET_SCHEDULE_OBJ_PROP_REF_SIZE)) { + if (pMember) { + status = bacnet_device_object_property_reference_copy( + &pObject->Object_Property_References[index], pMember); + } + } + + return status; +} + +/** + * @brief Set a member element of a given BACnetLIST object property + * @param pObject - object in which to set the value + * @param index - 0-based array index + * @param pMember - pointer to member value + * @return true if set, false if not set + */ +bool Schedule_List_Of_Object_Property_References_Set( + uint32_t object_instance, + unsigned index, + const BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember) +{ + bool status = false; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + status = List_Of_Object_Property_References_Set(pObject, index, pMember); + + return status; +} + +/** + * @brief Set a member element of a given BACnetLIST object property + * @param pObject - object in which to set the value + * @param index - 0-based array index + * @param pMember - pointer to member value + * @return true if set, false if not set + */ +bool Schedule_List_Of_Object_Property_References( + uint32_t object_instance, + unsigned index, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember) +{ + bool status = false; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject && (index < BACNET_SCHEDULE_OBJ_PROP_REF_SIZE)) { + if (pMember) { + status = bacnet_device_object_property_reference_copy( + pMember, &pObject->Object_Property_References[index]); + } + } + + return status; +} + +/** + * @brief Get the size of the list of object property references + * @param object_instance [in] BACnet network port object instance number + * @return The size of the list of object property references + */ +size_t +Schedule_List_Of_Object_Property_References_Capacity(uint32_t object_instance) +{ + (void)object_instance; /* unused */ + return BACNET_SCHEDULE_OBJ_PROP_REF_SIZE; +} + +/** + * @brief Read a property from the Schedule object + * @param rpdata [in] pointer to the read property data structure + * @return The length of the apdu encoded or BACNET_STATUS_ERROR + */ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) { int apdu_len = 0; @@ -342,7 +521,7 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) uint16_t apdu_max = 0; BACNET_BIT_STRING bit_string; BACNET_CHARACTER_STRING char_string; - int i; + int i, imax = 0; if ((rpdata == NULL) || (rpdata->application_data == NULL) || (rpdata->application_data_len == 0)) { @@ -411,7 +590,9 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) bacapp_encode_data(&apdu[0], &CurrentSC->Schedule_Default); break; case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: - for (i = 0; i < CurrentSC->obj_prop_ref_cnt; i++) { + imax = min( + CurrentSC->obj_prop_ref_cnt, BACNET_SCHEDULE_OBJ_PROP_REF_SIZE); + for (i = 0; i < imax; i++) { apdu_len += bacapp_encode_device_obj_property_ref( &apdu[apdu_len], &CurrentSC->Object_Property_References[i]); } @@ -523,6 +704,153 @@ static int Schedule_Weekly_Schedule_Element_Length( return len; } +#if BACNET_EXCEPTION_SCHEDULE_SIZE +/** + * @brief Write a value to a BACnetARRAY property element value + * @param object_instance [in] BACnet network port object instance number + * @param array_index [in] array index to write: + * 0=array size, 1 to N for individual array members + * @param application_data [in] encoded element value + * @param application_data_len [in] The size of the encoded element value + * @return BACNET_ERROR_CODE value + */ +static BACNET_ERROR_CODE Schedule_Exception_Schedule_Element_Write( + uint32_t object_instance, + BACNET_ARRAY_INDEX array_index, + uint8_t *application_data, + size_t application_data_len) +{ + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_SPECIAL_EVENT special_event = { 0 }; + int len = 0; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + if (array_index == 0) { + error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } else if (array_index <= BACNET_WEEKLY_SCHEDULE_SIZE) { + array_index--; + len = bacnet_special_event_decode( + application_data, application_data_len, &special_event); + if (len > 0) { + bacnet_special_event_copy( + &pObject->Exception_Schedule[array_index], &special_event); + error_code = ERROR_CODE_SUCCESS; + } else { + error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + } else { + error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + } + + return error_code; +} + +/** + * @brief Decode one BACnetARRAY property element + * @param object_instance [in] BACnet network port object instance number + * @param apdu [in] Buffer in which the APDU contents are extracted + * @param apdu_size [in] The size of the APDU buffer + * @return The length of the decoded apdu, or BACNET_STATUS_ERROR on error + */ +static int Schedule_Exception_Schedule_Element_Length( + uint32_t object_instance, uint8_t *apdu, size_t apdu_size) +{ + BACNET_SPECIAL_EVENT special_event = { 0 }; + int len = 0; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + len = bacnet_special_event_decode(apdu, apdu_size, &special_event); + } + + return len; +} +#endif + +/** + * @brief Write a value to a BACnetLIST property element value + * using a BACnetARRAY write utility function + * @param object_instance [in] BACnet network port object instance number + * @param array_index [in] array index to write: + * 0=array size, 1 to N for individual array members + * @param application_data [in] encoded element value + * @param application_data_len [in] The size of the encoded element value + * @return BACNET_ERROR_CODE value + */ +static BACNET_ERROR_CODE Schedule_List_Of_Object_Property_References_Write( + uint32_t object_instance, + BACNET_ARRAY_INDEX array_index, + uint8_t *application_data, + size_t application_data_len) +{ + BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT; + BACNET_APPLICATION_DATA_VALUE value = { 0 }; + int len = 0; + bool status; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + if (array_index == 0) { + error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY; + } else if (array_index <= BACNET_SCHEDULE_OBJ_PROP_REF_SIZE) { + len = bacapp_decode_known_property( + application_data, application_data_len, &value, OBJECT_SCHEDULE, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES); + if (len > 0) { + if (value.tag == + BACNET_APPLICATION_TAG_DEVICE_OBJECT_PROPERTY_REFERENCE) { + status = List_Of_Object_Property_References_Set( + pObject, array_index - 1, + &value.type.Device_Object_Property_Reference); + if (status) { + pObject->obj_prop_ref_cnt = array_index; + error_code = ERROR_CODE_SUCCESS; + } else { + error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + } else { + error_code = ERROR_CODE_INVALID_DATA_TYPE; + } + } else { + error_code = ERROR_CODE_ABORT_OTHER; + } + } else { + error_code = ERROR_CODE_INVALID_ARRAY_INDEX; + } + } + + return error_code; +} + +/** + * @brief Decode a BACnetLIST property element to determine the element length + * @param object_instance [in] BACnet network port object instance number + * @param apdu [in] Buffer in which the APDU contents are extracted + * @param apdu_size [in] The size of the APDU buffer + * @return The length of the decoded apdu, or BACNET_STATUS_ERROR on error + */ +static int Schedule_List_Of_Object_Property_References_Length( + uint32_t object_instance, uint8_t *apdu, size_t apdu_size) +{ + BACNET_APPLICATION_DATA_VALUE value = { 0 }; + int len = 0; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + len = bacapp_decode_known_property( + apdu, apdu_size, &value, OBJECT_SCHEDULE, + PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES); + } + + return len; +} + /** * @brief Write a property to the Schedule object * @param wp_data - pointer to the write property data @@ -569,6 +897,43 @@ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) status = true; } break; + case PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES: + wp_data->error_code = bacnet_array_write( + wp_data->object_instance, wp_data->array_index, + Schedule_List_Of_Object_Property_References_Length, + Schedule_List_Of_Object_Property_References_Write, + BACNET_SCHEDULE_OBJ_PROP_REF_SIZE, wp_data->application_data, + wp_data->application_data_len); + if (wp_data->error_code == ERROR_CODE_SUCCESS) { + status = true; + } + break; + case PROP_EFFECTIVE_PERIOD: + status = write_property_type_valid( + wp_data, &value, BACNET_APPLICATION_TAG_DATERANGE); + if (status) { + /* set the start and end date */ + datetime_copy_date( + &Schedule_Descr[object_index].Start_Date, + &value.type.Date_Range.startdate); + datetime_copy_date( + &Schedule_Descr[object_index].End_Date, + &value.type.Date_Range.enddate); + } + break; +#if BACNET_EXCEPTION_SCHEDULE_SIZE + case PROP_EXCEPTION_SCHEDULE: + wp_data->error_code = bacnet_array_write( + wp_data->object_instance, wp_data->array_index, + Schedule_Exception_Schedule_Element_Length, + Schedule_Exception_Schedule_Element_Write, + BACNET_EXCEPTION_SCHEDULE_SIZE, wp_data->application_data, + wp_data->application_data_len); + if (wp_data->error_code == ERROR_CODE_SUCCESS) { + status = true; + } + break; +#endif default: if (property_lists_member( Schedule_Properties_Required, Schedule_Properties_Optional, diff --git a/src/bacnet/basic/object/schedule.h b/src/bacnet/basic/object/schedule.h index 4cdbc6bf..a109bb2d 100644 --- a/src/bacnet/basic/object/schedule.h +++ b/src/bacnet/basic/object/schedule.h @@ -60,6 +60,8 @@ typedef struct schedule { bool Out_Of_Service; } SCHEDULE_DESCR; +BACNET_STACK_EXPORT +struct schedule *Schedule_Object(uint32_t object_instance); BACNET_STACK_EXPORT void Schedule_Property_Lists( const int **pRequired, const int **pOptional, const int **pProprietary); @@ -89,6 +91,38 @@ bool Schedule_Weekly_Schedule_Set( unsigned array_index, const BACNET_DAILY_SCHEDULE *value); +BACNET_STACK_EXPORT +BACNET_SPECIAL_EVENT * +Schedule_Exception_Schedule(uint32_t object_instance, unsigned array_index); +BACNET_STACK_EXPORT +bool Schedule_Exception_Schedule_Set( + uint32_t object_instance, + unsigned array_index, + const BACNET_SPECIAL_EVENT *value); + +BACNET_STACK_EXPORT +bool Schedule_List_Of_Object_Property_References_Set( + uint32_t object_instance, + unsigned index, + const BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember); +BACNET_STACK_EXPORT +bool Schedule_List_Of_Object_Property_References( + uint32_t object_instance, + unsigned index, + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *pMember); +BACNET_STACK_EXPORT +size_t +Schedule_List_Of_Object_Property_References_Capacity(uint32_t object_instance); + +BACNET_STACK_EXPORT +bool Schedule_Effective_Period_Set( + uint32_t object_instance, + const BACNET_DATE *start_date, + const BACNET_DATE *end_date); +BACNET_STACK_EXPORT +bool Schedule_Effective_Period( + uint32_t object_instance, BACNET_DATE *start_date, BACNET_DATE *end_date); + BACNET_STACK_EXPORT bool Schedule_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name); diff --git a/src/bacnet/dailyschedule.c b/src/bacnet/dailyschedule.c index b3c6664e..1197b46c 100644 --- a/src/bacnet/dailyschedule.c +++ b/src/bacnet/dailyschedule.c @@ -64,3 +64,51 @@ int bacnet_dailyschedule_context_encode( return bacnet_time_values_context_encode( apdu, tag_number, &day->Time_Values[0], day->TV_Count); } + +/** + * @brief Compare two BACnetDailySchedule values + * @param a [in] First value to compare + * @param b [in] Second value to compare + * @return true if the values are the same, false otherwise + * @note If either value is NULL, false is returned. + */ +bool bacnet_dailyschedule_same( + const BACNET_DAILY_SCHEDULE *a, const BACNET_DAILY_SCHEDULE *b) +{ + unsigned i; + + if (a == NULL || b == NULL) { + return false; + } + if (a->TV_Count != b->TV_Count) { + return false; + } + for (i = 0; i < a->TV_Count; i++) { + if (!bacnet_time_value_same(&a->Time_Values[i], &b->Time_Values[i])) { + return false; + } + } + return true; +} + +/** + * @brief Copy a BACnetDailySchedule value + * @param dest [out] Destination to copy to + * @param src [in] Source to copy from + */ +void bacnet_dailyschedule_copy( + BACNET_DAILY_SCHEDULE *dest, const BACNET_DAILY_SCHEDULE *src) +{ + unsigned i; + + if (dest == NULL || src == NULL) { + return; + } + if (src->TV_Count > ARRAY_SIZE(dest->Time_Values)) { + return; + } + dest->TV_Count = src->TV_Count; + for (i = 0; i < dest->TV_Count; i++) { + bacnet_time_value_copy(&dest->Time_Values[i], &src->Time_Values[i]); + } +} diff --git a/src/bacnet/dailyschedule.h b/src/bacnet/dailyschedule.h index 10c5dd4c..c041ac1c 100644 --- a/src/bacnet/dailyschedule.h +++ b/src/bacnet/dailyschedule.h @@ -52,6 +52,14 @@ BACNET_STACK_EXPORT int bacnet_dailyschedule_context_encode( uint8_t *apdu, uint8_t tag_number, const BACNET_DAILY_SCHEDULE *day); +BACNET_STACK_EXPORT +bool bacnet_dailyschedule_same( + const BACNET_DAILY_SCHEDULE *a, const BACNET_DAILY_SCHEDULE *b); + +BACNET_STACK_EXPORT +void bacnet_dailyschedule_copy( + BACNET_DAILY_SCHEDULE *dest, const BACNET_DAILY_SCHEDULE *src); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/special_event.c b/src/bacnet/special_event.c index 9f22acb4..bfb676ec 100644 --- a/src/bacnet/special_event.c +++ b/src/bacnet/special_event.c @@ -265,3 +265,21 @@ bool bacnet_special_event_same( return true; } + +/** + * @brief Compare the BACnetSpecialEvent complex data + * @param value1 - BACNET_SPECIAL_EVENT structure + * @param value2 - BACNET_SPECIAL_EVENT structure + * @return true if the same + */ +bool bacnet_special_event_copy( + BACNET_SPECIAL_EVENT *dest, const BACNET_SPECIAL_EVENT *src) +{ + if (!dest || !src) { + return false; + } + + memcpy(dest, src, sizeof(BACNET_SPECIAL_EVENT)); + + return true; +} diff --git a/src/bacnet/special_event.h b/src/bacnet/special_event.h index d6106555..55132b78 100644 --- a/src/bacnet/special_event.h +++ b/src/bacnet/special_event.h @@ -64,6 +64,10 @@ BACNET_STACK_EXPORT bool bacnet_special_event_same( const BACNET_SPECIAL_EVENT *value1, const BACNET_SPECIAL_EVENT *value2); +BACNET_STACK_EXPORT +bool bacnet_special_event_copy( + BACNET_SPECIAL_EVENT *dest, const BACNET_SPECIAL_EVENT *src); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/test/bacnet/basic/object/schedule/src/main.c b/test/bacnet/basic/object/schedule/src/main.c index 082ce03b..bb957156 100644 --- a/test/bacnet/basic/object/schedule/src/main.c +++ b/test/bacnet/basic/object/schedule/src/main.c @@ -28,30 +28,100 @@ static void testSchedule(void) unsigned count = 0; uint32_t object_instance = 0; const int skip_fail_property_list[] = { -1 }; - BACNET_DAILY_SCHEDULE *daily_schedule; - size_t tv = 0, day = 0; + BACNET_DAILY_SCHEDULE daily_schedule = { 0 }, *test_daily_schedule; + BACNET_SPECIAL_EVENT special_event = { 0 }, *test_special_event; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE object_property_reference = { 0 }, + test_object_property_reference = { + 0 + }; + BACNET_DATE start_date = { 2023, 1, 1, 0 }, test_start_date = { 0 }; + BACNET_DATE end_date = { 2023, 12, 31, 0 }, test_end_date = { 0 }; + BACNET_TIME time_of_day = { 0 }; + size_t tv = 0, day = 0, i = 0; + int diff; + bool status = false; Schedule_Init(); count = Schedule_Count(); zassert_true(count > 0, NULL); object_instance = Schedule_Index_To_Instance(0); + status = Schedule_Valid_Instance(object_instance); + zassert_true(status, NULL); + /* fill the weekly schedule with some data */ for (day = 0; day < BACNET_WEEKLY_SCHEDULE_SIZE; day++) { - daily_schedule = Schedule_Weekly_Schedule(object_instance, day); - daily_schedule->TV_Count = BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE; - for (tv = 0; tv < daily_schedule->TV_Count; tv++) { + daily_schedule.TV_Count = BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE; + for (tv = 0; tv < daily_schedule.TV_Count; tv++) { datetime_set_time( - &daily_schedule->Time_Values[tv].Time, tv % 24, 0, 0, 0); - daily_schedule->Time_Values[tv].Value.tag = + &daily_schedule.Time_Values[tv].Time, tv % 24, 0, 0, 0); + daily_schedule.Time_Values[tv].Value.tag = BACNET_APPLICATION_TAG_REAL; - daily_schedule->Time_Values[tv].Value.type.Real = 1.0f + tv; + daily_schedule.Time_Values[tv].Value.type.Real = 1.0f + tv; } + Schedule_Weekly_Schedule_Set(object_instance, day, &daily_schedule); + test_daily_schedule = Schedule_Weekly_Schedule(object_instance, day); + status = + bacnet_dailyschedule_same(&daily_schedule, test_daily_schedule); + zassert_true(status, NULL); } + for (i = 0; i < BACNET_EXCEPTION_SCHEDULE_SIZE; i++) { + special_event.periodTag = BACNET_SPECIAL_EVENT_PERIOD_CALENDAR_ENTRY; + special_event.timeValues.TV_Count = + BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE; + for (tv = 0; tv < special_event.timeValues.TV_Count; tv++) { + datetime_set_time( + &special_event.timeValues.Time_Values[tv].Time, tv % 24, 0, 0, + 0); + special_event.timeValues.Time_Values[tv].Value.tag = + BACNET_APPLICATION_TAG_REAL; + special_event.timeValues.Time_Values[tv].Value.type.Real = + 1.0f + tv; + special_event.priority = tv % (BACNET_MAX_PRIORITY + 1); + } + Schedule_Exception_Schedule_Set(object_instance, i, &special_event); + test_special_event = Schedule_Exception_Schedule(object_instance, i); + status = bacnet_special_event_same(&special_event, test_special_event); + zassert_true(status, NULL); + } + /* fill the object property reference with some data */ + for (i = 0; i < + Schedule_List_Of_Object_Property_References_Capacity(object_instance); + i++) { + object_property_reference.objectIdentifier.type = OBJECT_ANALOG_VALUE; + object_property_reference.objectIdentifier.instance = i + 1; + object_property_reference.propertyIdentifier = PROP_PRESENT_VALUE; + object_property_reference.arrayIndex = BACNET_ARRAY_ALL; + Schedule_List_Of_Object_Property_References_Set( + object_instance, i, &object_property_reference); + Schedule_List_Of_Object_Property_References( + object_instance, i, &test_object_property_reference); + status = bacnet_device_object_property_reference_same( + &object_property_reference, &test_object_property_reference); + zassert_true(status, NULL); + } + status = + Schedule_Effective_Period_Set(object_instance, &start_date, &end_date); + zassert_true(status, NULL); + status = Schedule_Effective_Period( + object_instance, &test_start_date, &test_end_date); + zassert_true(status, NULL); + diff = datetime_compare_date(&start_date, &test_start_date); + zassert_equal(diff, 0, NULL); + diff = datetime_compare_date(&end_date, &test_end_date); + zassert_equal(diff, 0, NULL); + status = Schedule_In_Effective_Period( + Schedule_Object(object_instance), &start_date); + zassert_true(status, NULL); + status = Schedule_In_Effective_Period( + Schedule_Object(object_instance), &end_date); + zassert_true(status, NULL); /* general purpose test */ bacnet_object_properties_read_write_test( OBJECT_SCHEDULE, object_instance, Schedule_Property_Lists, Schedule_Read_Property, Schedule_Write_Property, skip_fail_property_list); + Schedule_Recalculate_PV( + Schedule_Object(object_instance), BACNET_WEEKDAY_SUNDAY, &time_of_day); } /** * @}