From 1a7d8239595bed1b7e0ebf3a49276da984ef4c18 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Thu, 8 May 2025 08:15:16 -0500 Subject: [PATCH] Added the weekly-schedule property write in basic schedule object. (#990) --- src/bacnet/basic/object/schedule.c | 185 +++++++++++++++--- src/bacnet/basic/object/schedule.h | 27 ++- src/bacnet/dailyschedule.h | 6 +- src/bacnet/weeklyschedule.c | 7 +- src/bacnet/weeklyschedule.h | 3 +- .../basic/object/schedule/CMakeLists.txt | 1 + test/bacnet/basic/object/schedule/src/main.c | 15 ++ 7 files changed, 195 insertions(+), 49 deletions(-) diff --git a/src/bacnet/basic/object/schedule.c b/src/bacnet/basic/object/schedule.c index 603c6d75..00469dbe 100644 --- a/src/bacnet/basic/object/schedule.c +++ b/src/bacnet/basic/object/schedule.c @@ -15,6 +15,7 @@ #include "bacnet/proplist.h" #include "bacnet/timestamp.h" #include "bacnet/basic/services.h" +#include "bacnet/basic/sys/debug.h" #include "bacnet/basic/object/device.h" #include "bacnet/basic/object/schedule.h" @@ -25,6 +26,7 @@ static SCHEDULE_DESCR Schedule_Descr[MAX_SCHEDULES]; static const int Schedule_Properties_Required[] = { + /* list of required properties */ PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE, @@ -39,11 +41,14 @@ static const int Schedule_Properties_Required[] = { -1 }; -static const int Schedule_Properties_Optional[] = { PROP_WEEKLY_SCHEDULE, +static const int Schedule_Properties_Optional[] = { + /* list of optional properties */ + PROP_WEEKLY_SCHEDULE, #if BACNET_EXCEPTION_SCHEDULE_SIZE - PROP_EXCEPTION_SCHEDULE, + PROP_EXCEPTION_SCHEDULE, #endif - -1 }; + -1 +}; static const int Schedule_Properties_Proprietary[] = { -1 }; @@ -103,7 +108,7 @@ void Schedule_Init(void) psched = &Schedule_Descr[i]; datetime_copy_date(&psched->Start_Date, &start_date); datetime_copy_date(&psched->End_Date, &end_date); - for (j = 0; j < 7; j++) { + for (j = 0; j < BACNET_WEEKLY_SCHEDULE_SIZE; j++) { psched->Weekly_Schedule[j].TV_Count = 0; } memcpy( @@ -223,6 +228,50 @@ void Schedule_Out_Of_Service_Set(uint32_t object_instance, bool value) } } +/** + * @brief Get the Weekly Schedule for a given object instance + * @param object_instance - object-instance number of the object + * @param array_index - index of the Weekly Schedule to get 0 to 6 + * @return pointer to the Weekly Schedule, or NULL if not found + */ +BACNET_DAILY_SCHEDULE * +Schedule_Weekly_Schedule(uint32_t object_instance, unsigned array_index) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject && (array_index < BACNET_WEEKLY_SCHEDULE_SIZE)) { + return &pObject->Weekly_Schedule[array_index]; + } + + return NULL; +} + +/** + * @brief Set the Weekly Schedule for a given object instance + * @param object_instance - object-instance number of the object + * @param array_index - index of the Weekly Schedule to set 0 to 6 + * @param value - pointer to the Weekly Schedule to set + * @return true if the Weekly Schedule was set, and false if not + */ +bool Schedule_Weekly_Schedule_Set( + uint32_t object_instance, + unsigned array_index, + const BACNET_DAILY_SCHEDULE *value) +{ + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject && (array_index < BACNET_WEEKLY_SCHEDULE_SIZE)) { + memcpy( + &pObject->Weekly_Schedule[array_index], value, + sizeof(BACNET_WEEKLY_SCHEDULE)); + return true; + } + + return false; +} + /** * @brief Encode a BACnetARRAY property element * @param object_instance [in] BACnet network port object instance number @@ -236,33 +285,19 @@ void Schedule_Out_Of_Service_Set(uint32_t object_instance, bool value) static int Schedule_Weekly_Schedule_Encode( uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu) { - int apdu_len = 0, len = 0; + int apdu_len; SCHEDULE_DESCR *pObject; - int day, i; - if (array_index >= 7) { + if (array_index >= BACNET_WEEKLY_SCHEDULE_SIZE) { return BACNET_STATUS_ERROR; } pObject = Schedule_Object(object_instance); if (!pObject) { return BACNET_STATUS_ERROR; } - day = array_index; - len = encode_opening_tag(apdu, 0); - apdu_len += len; - if (apdu) { - apdu += len; - } - for (i = 0; i < pObject->Weekly_Schedule[day].TV_Count; i++) { - len = bacnet_time_value_encode( - apdu, &pObject->Weekly_Schedule[day].Time_Values[i]); - apdu_len += len; - if (apdu) { - apdu += len; - } - } - len = encode_closing_tag(apdu, 0); - apdu_len += len; + + apdu_len = bacnet_dailyschedule_context_encode( + apdu, 0, &pObject->Weekly_Schedule[array_index]); return apdu_len; } @@ -346,7 +381,8 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) case PROP_WEEKLY_SCHEDULE: apdu_len = bacnet_array_encode( rpdata->object_instance, rpdata->array_index, - Schedule_Weekly_Schedule_Encode, 7, apdu, apdu_max); + Schedule_Weekly_Schedule_Encode, BACNET_WEEKLY_SCHEDULE_SIZE, + apdu, apdu_max); if (apdu_len == BACNET_STATUS_ABORT) { rpdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; @@ -411,6 +447,87 @@ int Schedule_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } +/** + * @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_Weekly_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_DAILY_SCHEDULE daily_schedule = { 0 }; + size_t tv, tv_size; + 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_dailyschedule_context_decode( + application_data, application_data_len, 0, &daily_schedule); + if (len > 0) { + tv_size = + min(daily_schedule.TV_Count, + BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE); + for (tv = 0; tv < tv_size; tv++) { + /* copy the time value */ + memcpy( + &pObject->Weekly_Schedule[array_index].Time_Values[tv], + &daily_schedule.Time_Values[tv], + sizeof(BACNET_TIME_VALUE)); + } + pObject->Weekly_Schedule[array_index].TV_Count = tv_size; + 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_Weekly_Schedule_Element_Length( + uint32_t object_instance, uint8_t *apdu, size_t apdu_size) +{ + BACNET_DAILY_SCHEDULE daily_schedule = { 0 }; + int len = 0; + SCHEDULE_DESCR *pObject; + + pObject = Schedule_Object(object_instance); + if (pObject) { + len = bacnet_dailyschedule_context_decode( + apdu, apdu_size, 0, &daily_schedule); + } + + return len; +} + +/** + * @brief Write a property to the Schedule object + * @param wp_data - pointer to the write property data + * @return true if the write was successful, and false if not + */ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) { unsigned object_index; @@ -419,9 +536,9 @@ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) BACNET_APPLICATION_DATA_VALUE value = { 0 }; /* 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? */ + len = bacapp_decode_known_array_property( + wp_data->application_data, wp_data->application_data_len, &value, + wp_data->object_type, wp_data->object_property, wp_data->array_index); if (len < 0) { /* error while decoding - a value larger than we can handle */ wp_data->error_class = ERROR_CLASS_PROPERTY; @@ -441,11 +558,25 @@ bool Schedule_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) wp_data->object_instance, value.type.Boolean); } break; + case PROP_WEEKLY_SCHEDULE: + wp_data->error_code = bacnet_array_write( + wp_data->object_instance, wp_data->array_index, + Schedule_Weekly_Schedule_Element_Length, + Schedule_Weekly_Schedule_Element_Write, + BACNET_WEEKLY_SCHEDULE_SIZE, wp_data->application_data, + wp_data->application_data_len); + if (wp_data->error_code == ERROR_CODE_SUCCESS) { + status = true; + } + break; default: if (property_lists_member( Schedule_Properties_Required, Schedule_Properties_Optional, Schedule_Properties_Proprietary, wp_data->object_property)) { + debug_printf( + "Schedule_Write_Property: %s\n", + bactext_property_name(wp_data->object_property)); wp_data->error_class = ERROR_CLASS_PROPERTY; wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; } else { diff --git a/src/bacnet/basic/object/schedule.h b/src/bacnet/basic/object/schedule.h index d7e6d0ac..4cdbc6bf 100644 --- a/src/bacnet/basic/object/schedule.h +++ b/src/bacnet/basic/object/schedule.h @@ -19,13 +19,9 @@ #include "bacnet/rp.h" #include "bacnet/bacdevobjpropref.h" #include "bacnet/bactimevalue.h" +#include "bacnet/dailyschedule.h" #include "bacnet/special_event.h" -#ifndef BACNET_WEEKLY_SCHEDULE_SIZE -/* Maximum number of data points for each day */ -#define BACNET_WEEKLY_SCHEDULE_SIZE 8 -#endif - #ifndef BACNET_SCHEDULE_OBJ_PROP_REF_SIZE /* Maximum number of obj prop references */ #define BACNET_SCHEDULE_OBJ_PROP_REF_SIZE 4 @@ -40,22 +36,12 @@ extern "C" { #endif /* __cplusplus */ -/* - * Note: - * This is a different struct from BACNET_DAILY_SCHEDULE used in prop value - * encoding! The number of entries is different. - */ -typedef struct bacnet_obj_daily_schedule { - BACNET_TIME_VALUE Time_Values[BACNET_WEEKLY_SCHEDULE_SIZE]; - uint16_t TV_Count; /* the number of time values actually used */ -} BACNET_OBJ_DAILY_SCHEDULE; - typedef struct schedule { /* Effective Period: Start and End Date */ BACNET_DATE Start_Date; BACNET_DATE End_Date; /* Properties concerning Present Value */ - BACNET_OBJ_DAILY_SCHEDULE Weekly_Schedule[7]; + BACNET_DAILY_SCHEDULE Weekly_Schedule[BACNET_WEEKLY_SCHEDULE_SIZE]; #if BACNET_EXCEPTION_SCHEDULE_SIZE BACNET_SPECIAL_EVENT Exception_Schedule[BACNET_EXCEPTION_SCHEDULE_SIZE]; #endif @@ -94,6 +80,15 @@ void Schedule_Out_Of_Service_Set(uint32_t object_instance, bool value); BACNET_STACK_EXPORT bool Schedule_Out_Of_Service(uint32_t object_instance); +BACNET_STACK_EXPORT +BACNET_DAILY_SCHEDULE * +Schedule_Weekly_Schedule(uint32_t object_instance, unsigned array_index); +BACNET_STACK_EXPORT +bool Schedule_Weekly_Schedule_Set( + uint32_t object_instance, + unsigned array_index, + const BACNET_DAILY_SCHEDULE *value); + BACNET_STACK_EXPORT bool Schedule_Object_Name( uint32_t object_instance, BACNET_CHARACTER_STRING *object_name); diff --git a/src/bacnet/dailyschedule.h b/src/bacnet/dailyschedule.h index dd7d4c98..10c5dd4c 100644 --- a/src/bacnet/dailyschedule.h +++ b/src/bacnet/dailyschedule.h @@ -20,7 +20,9 @@ * be enough */ /* however we try not to boost the bacnet application value structure size, */ /* so 7 x (this value) x sizeof(BACNET_TIME_VALUE) fits. */ -#define MAX_DAY_SCHEDULE_VALUES 40 +#ifndef BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE +#define BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE 40 +#endif /* BACnetDailySchedule ::= SEQUENCE { @@ -33,7 +35,7 @@ extern "C" { #endif /* __cplusplus */ typedef struct BACnet_Daily_Schedule { - BACNET_TIME_VALUE Time_Values[MAX_DAY_SCHEDULE_VALUES]; + BACNET_TIME_VALUE Time_Values[BACNET_DAILY_SCHEDULE_TIME_VALUES_SIZE]; uint16_t TV_Count; /* the number of time values actually used */ } BACNET_DAILY_SCHEDULE; diff --git a/src/bacnet/weeklyschedule.c b/src/bacnet/weeklyschedule.c index a8aa36ea..794a4c05 100644 --- a/src/bacnet/weeklyschedule.c +++ b/src/bacnet/weeklyschedule.c @@ -32,7 +32,7 @@ int bacnet_weeklyschedule_decode( return BACNET_STATUS_ERROR; } value->singleDay = false; - for (wi = 0; wi < 7; wi++) { + for (wi = 0; wi < BACNET_WEEKLY_SCHEDULE_SIZE; wi++) { len = bacnet_dailyschedule_context_decode( &apdu[apdu_len], apdu_size - apdu_len, 0, &value->weeklySchedule[wi]); @@ -67,7 +67,8 @@ int bacnet_weeklyschedule_encode( int len = 0; int wi; - for (wi = 0; wi < (value->singleDay ? 1 : 7); wi++) { + for (wi = 0; wi < (value->singleDay ? 1 : BACNET_WEEKLY_SCHEDULE_SIZE); + wi++) { len = bacnet_dailyschedule_context_encode( apdu, 0, &value->weeklySchedule[wi]); if (len < 0) { @@ -176,7 +177,7 @@ bool bacnet_weeklyschedule_same( const BACNET_TIME_VALUE *tv1, *tv2; int wi, ti; - for (wi = 0; wi < 7; wi++) { + for (wi = 0; wi < BACNET_WEEKLY_SCHEDULE_SIZE; wi++) { ds1 = &value1->weeklySchedule[wi]; ds2 = &value2->weeklySchedule[wi]; if (ds1->TV_Count != ds2->TV_Count) { diff --git a/src/bacnet/weeklyschedule.h b/src/bacnet/weeklyschedule.h index 49e32279..4bc26837 100644 --- a/src/bacnet/weeklyschedule.h +++ b/src/bacnet/weeklyschedule.h @@ -20,8 +20,9 @@ extern "C" { #endif /* __cplusplus */ +#define BACNET_WEEKLY_SCHEDULE_SIZE 7 typedef struct BACnet_Weekly_Schedule { - BACNET_DAILY_SCHEDULE weeklySchedule[7]; + BACNET_DAILY_SCHEDULE weeklySchedule[BACNET_WEEKLY_SCHEDULE_SIZE]; bool singleDay; } BACNET_WEEKLY_SCHEDULE; diff --git a/test/bacnet/basic/object/schedule/CMakeLists.txt b/test/bacnet/basic/object/schedule/CMakeLists.txt index 5ffbbc21..7d188562 100644 --- a/test/bacnet/basic/object/schedule/CMakeLists.txt +++ b/test/bacnet/basic/object/schedule/CMakeLists.txt @@ -51,6 +51,7 @@ add_executable(${PROJECT_NAME} ${SRC_DIR}/bacnet/basic/sys/bigend.c ${SRC_DIR}/bacnet/datetime.c ${SRC_DIR}/bacnet/basic/sys/days.c + ${SRC_DIR}/bacnet/basic/sys/debug.c ${SRC_DIR}/bacnet/indtext.c ${SRC_DIR}/bacnet/hostnport.c ${SRC_DIR}/bacnet/lighting.c diff --git a/test/bacnet/basic/object/schedule/src/main.c b/test/bacnet/basic/object/schedule/src/main.c index 5a6b00a2..082ce03b 100644 --- a/test/bacnet/basic/object/schedule/src/main.c +++ b/test/bacnet/basic/object/schedule/src/main.c @@ -28,11 +28,26 @@ 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; Schedule_Init(); count = Schedule_Count(); zassert_true(count > 0, NULL); object_instance = Schedule_Index_To_Instance(0); + /* 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++) { + datetime_set_time( + &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; + } + } + /* general purpose test */ bacnet_object_properties_read_write_test( OBJECT_SCHEDULE, object_instance, Schedule_Property_Lists, Schedule_Read_Property, Schedule_Write_Property,