From 89cf25c2b2c0591c67cbc51ddc767d8948758fde Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 20 Jan 2026 13:13:45 -0600 Subject: [PATCH] Added post-write notifications for channel, timer, and loop objects. (#1204) --- CHANGELOG.md | 1 + src/bacnet/basic/object/channel.c | 51 ++++++++++++++- src/bacnet/basic/object/channel.h | 21 ++++++ src/bacnet/basic/object/loop.c | 46 +++++++++++++ src/bacnet/basic/object/loop.h | 21 ++++++ src/bacnet/basic/object/timer.c | 72 +++++++++++++++++---- src/bacnet/basic/object/timer.h | 21 ++++++ test/bacnet/basic/object/channel/src/main.c | 64 +++++++++++++++++- test/bacnet/basic/object/loop/src/main.c | 56 ++++++++++++++++ test/bacnet/basic/object/timer/src/main.c | 27 ++++++++ 10 files changed, 367 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e8f96d..bbfeaef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ The git repositories are hosted at the following sites: ### Added +* Added post-write notifications for channel, timer, and loop objects. (#1204) * Added device WriteProperty callbacks for Timer object in example device objects implementations. (#1203) * Added file path name checking for AtomicReadFile and AtomicWriteFile diff --git a/src/bacnet/basic/object/channel.c b/src/bacnet/basic/object/channel.c index f7bd8e1d..9ae373c4 100644 --- a/src/bacnet/basic/object/channel.c +++ b/src/bacnet/basic/object/channel.c @@ -52,8 +52,11 @@ struct object_data { /* Key List for storing the object data sorted by instance number */ static OS_Keylist Object_List; - +/* Internal write property callback */ static write_property_function Write_Property_Internal_Callback; +/* Write Property notification callbacks for logging or other purposes */ +static struct channel_write_property_notification + Write_Property_Notification_Head; /* These arrays are used by the ReadPropertyMultiple handler property-list property (as of protocol-revision 14) */ @@ -735,12 +738,15 @@ static bool Channel_Write_Members( bactext_error_code_name(wp_data.error_code)); } } else { + wp_data.error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE; debug_printf( "channel[%lu].Channel_Write_Member[%u] " "coercion failed!\n", (unsigned long)object_instance, m); pObject->Write_Status = BACNET_WRITE_STATUS_FAILED; } + Channel_Write_Property_Notify( + object_instance, status, &wp_data); } else { debug_printf( "channel[%lu].Channel_Write_Member[%u] invalid!\n", @@ -1380,6 +1386,49 @@ void Channel_Write_Property_Internal_Callback_Set(write_property_function cb) Write_Property_Internal_Callback = cb; } +/** + * @brief Add a Channel notification callback + * @param notification - pointer to the notification structure + */ +void Channel_Write_Property_Notification_Add( + struct channel_write_property_notification *notification) +{ + struct channel_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->next == notification) { + /* already here! */ + break; + } else if (!head->next) { + /* first available node */ + head->next = notification; + break; + } + head = head->next; + } while (head); +} + +/** + * @brief call the channel WriteProperty notification callbacks + * @param instance - object instance number + * @param status - status of the write + * @param wp_data - write property data + */ +void Channel_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + struct channel_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->callback) { + head->callback(instance, status, wp_data); + } + head = head->next; + } while (head); +} + /** * @brief Set the context used with a specific object instance * @param object_instance [in] BACnet object instance number diff --git a/src/bacnet/basic/object/channel.h b/src/bacnet/basic/object/channel.h index 9815a5a6..835a8722 100644 --- a/src/bacnet/basic/object/channel.h +++ b/src/bacnet/basic/object/channel.h @@ -19,6 +19,21 @@ #include "bacnet/basic/object/lo.h" #include "bacnet/channel_value.h" +/** + * @brief Callback for tracking the channel writes for logging or other purposes + * @param instance - channel object instance number + * @param status - true if write was successful + * @param wp_data - pointer to the write property data structure + */ +typedef void (*channel_write_property_callback)( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); +/* linked list structure for notifications */ +struct channel_write_property_notification; +struct channel_write_property_notification { + struct channel_write_property_notification *next; + channel_write_property_callback callback; +}; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -104,6 +119,12 @@ bool Channel_Write_Member_Value( BACNET_STACK_EXPORT void Channel_Write_Property_Internal_Callback_Set(write_property_function cb); +BACNET_STACK_EXPORT +void Channel_Write_Property_Notification_Add( + struct channel_write_property_notification *notification); +BACNET_STACK_EXPORT +void Channel_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); BACNET_STACK_EXPORT void *Channel_Context_Get(uint32_t object_instance); diff --git a/src/bacnet/basic/object/loop.c b/src/bacnet/basic/object/loop.c index 62ba6194..c73b7eed 100644 --- a/src/bacnet/basic/object/loop.c +++ b/src/bacnet/basic/object/loop.c @@ -40,6 +40,8 @@ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_LOOP; /* handling for manipulated and reference properties */ static write_property_function Write_Property_Internal_Callback; static read_property_function Read_Property_Internal_Callback; +/* Write Property notification callbacks for logging or other purposes */ +static struct loop_write_property_notification Write_Property_Notification_Head; struct object_data { /* internal variables for PID calculations */ @@ -2016,6 +2018,49 @@ void Loop_Write_Property_Internal_Callback_Set(write_property_function cb) Write_Property_Internal_Callback = cb; } +/** + * @brief Add a Loop notification callback + * @param notification - pointer to the notification structure + */ +void Loop_Write_Property_Notification_Add( + struct loop_write_property_notification *notification) +{ + struct loop_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->next == notification) { + /* already here! */ + break; + } else if (!head->next) { + /* first available node */ + head->next = notification; + break; + } + head = head->next; + } while (head); +} + +/** + * @brief Calls all registered Loop write property notification callbacks + * @param instance - object instance number + * @param status - write property status + * @param wp_data - write property data + */ +void Loop_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + struct loop_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->callback) { + head->callback(instance, status, wp_data); + } + head = head->next; + } while (head); +} + /** * @brief For a given object, writes to the manipulated-variable-reference * @param pObject - object instance data @@ -2059,6 +2104,7 @@ static bool Loop_Write_Manipulated_Variable( } } } + Loop_Write_Property_Notify(object_instance, status, &wp_data); } } diff --git a/src/bacnet/basic/object/loop.h b/src/bacnet/basic/object/loop.h index df1a6e22..e1f02897 100644 --- a/src/bacnet/basic/object/loop.h +++ b/src/bacnet/basic/object/loop.h @@ -18,6 +18,21 @@ #include "bacnet/rp.h" #include "bacnet/list_element.h" +/** + * @brief Callback for tracking the loop writes for logging or other purposes + * @param instance - loop object instance number + * @param status - true if write was successful + * @param wp_data - pointer to the write property data structure + */ +typedef void (*loop_write_property_callback)( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); +/* linked list structure for notifications */ +struct loop_write_property_notification; +struct loop_write_property_notification { + struct loop_write_property_notification *next; + loop_write_property_callback callback; +}; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -191,6 +206,12 @@ BACNET_STACK_EXPORT void Loop_Write_Property_Internal_Callback_Set(write_property_function cb); BACNET_STACK_EXPORT void Loop_Read_Property_Internal_Callback_Set(read_property_function cb); +BACNET_STACK_EXPORT +void Loop_Write_Property_Notification_Add( + struct loop_write_property_notification *notification); +BACNET_STACK_EXPORT +void Loop_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); BACNET_STACK_EXPORT int Loop_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata); diff --git a/src/bacnet/basic/object/timer.c b/src/bacnet/basic/object/timer.c index 078efafc..77296c88 100644 --- a/src/bacnet/basic/object/timer.c +++ b/src/bacnet/basic/object/timer.c @@ -39,6 +39,9 @@ static OS_Keylist Object_List = NULL; /* common object type */ static const BACNET_OBJECT_TYPE Object_Type = OBJECT_TIMER; static write_property_function Write_Property_Internal_Callback; +/* Write Property notification callbacks for logging or other purposes */ +static struct timer_write_property_notification + Write_Property_Notification_Head; struct object_data { uint32_t Present_Value; @@ -459,7 +462,7 @@ unsigned Timer_Reference_List_Member_Element_Count(uint32_t object_instance) /** * 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 pObject - object instance data * @param value - application value * @param priority - BACnet priority 0=none,1..16 @@ -467,6 +470,7 @@ unsigned Timer_Reference_List_Member_Element_Count(uint32_t object_instance) * @return true if values are within range and present-value is sent. */ static bool Timer_Write_Members( + uint32_t object_instance, struct object_data *pObject, const BACNET_TIMER_STATE_CHANGE_VALUE *value, uint8_t priority) @@ -504,6 +508,7 @@ static bool Timer_Write_Members( } } } + Timer_Write_Property_Notify(object_instance, status, &wp_data); } } } @@ -514,9 +519,11 @@ static bool Timer_Write_Members( /** * @brief initiate the write requests for the current transition * @param object_instance - object-instance number of the object + * @param pObject - object instance data * @return true if the write occurred */ -static bool Timer_Write_Request_Initiate(struct object_data *pObject) +static bool Timer_Write_Request_Initiate( + uint32_t object_instance, struct object_data *pObject) { bool status = false; unsigned index = 0; @@ -532,7 +539,7 @@ static bool Timer_Write_Request_Initiate(struct object_data *pObject) } if (value) { status = Timer_Write_Members( - pObject, value, pObject->Priority_For_Writing); + object_instance, pObject, value, pObject->Priority_For_Writing); } } @@ -600,7 +607,7 @@ bool Timer_State_Set(uint32_t object_instance, BACNET_TIMER_STATE value) &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); pObject->Last_State_Change = TIMER_TRANSITION_RUNNING_TO_IDLE; - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } else if (pObject->Timer_State == TIMER_STATE_EXPIRED) { pObject->Last_State_Change = TIMER_TRANSITION_EXPIRED_TO_IDLE; /* then set Timer_State to IDLE; @@ -614,7 +621,7 @@ bool Timer_State_Set(uint32_t object_instance, BACNET_TIMER_STATE value) &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); pObject->Last_State_Change = TIMER_TRANSITION_EXPIRED_TO_IDLE; - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } else if (pObject->Timer_State == TIMER_STATE_IDLE) { /* then no properties shall be changed; no write requests shall be initiated; @@ -695,7 +702,7 @@ bool Timer_Running_Set(uint32_t object_instance, bool start) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } else if (pObject->Timer_State == TIMER_STATE_RUNNING) { /* If a value of TRUE is written to the Timer_Running property, then set Last_State_Change to RUNNING_TO_RUNNING; @@ -712,7 +719,7 @@ bool Timer_Running_Set(uint32_t object_instance, bool start) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } else if (pObject->Timer_State == TIMER_STATE_EXPIRED) { /* If a value of TRUE is written to the Timer_Running property, set Timer_State to RUNNING; @@ -730,7 +737,7 @@ bool Timer_Running_Set(uint32_t object_instance, bool start) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } } else { if (pObject->Timer_State == TIMER_STATE_RUNNING) { @@ -749,7 +756,7 @@ bool Timer_Running_Set(uint32_t object_instance, bool start) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } } status = true; @@ -1079,7 +1086,7 @@ bool Timer_Present_Value_Set(uint32_t object_instance, uint32_t value) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); } status = true; } else { @@ -1112,7 +1119,7 @@ bool Timer_Present_Value_Set(uint32_t object_instance, uint32_t value) datetime_local( &pObject->Update_Time.date, &pObject->Update_Time.time, NULL, NULL); - Timer_Write_Request_Initiate(pObject); + Timer_Write_Request_Initiate(object_instance, pObject); status = true; } else { status = false; @@ -2082,6 +2089,49 @@ void Timer_Write_Property_Internal_Callback_Set(write_property_function cb) Write_Property_Internal_Callback = cb; } +/** + * @brief Add a Timer write property notification callback + * @param notification - pointer to the notification structure + */ +void Timer_Write_Property_Notification_Add( + struct timer_write_property_notification *notification) +{ + struct timer_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->next == notification) { + /* already here! */ + break; + } else if (!head->next) { + /* first available node */ + head->next = notification; + break; + } + head = head->next; + } while (head); +} + +/** + * @brief Calls all registered Timer write property notification callbacks + * @param instance - object instance number + * @param status - write property status + * @param wp_data - write property data + */ +void Timer_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + struct timer_write_property_notification *head; + + head = &Write_Property_Notification_Head; + do { + if (head->callback) { + head->callback(instance, status, wp_data); + } + head = head->next; + } while (head); +} + /** * @brief Updates the object program operation * @details In the RUNNING state, the timer is active diff --git a/src/bacnet/basic/object/timer.h b/src/bacnet/basic/object/timer.h index 1ba7baa3..0971025b 100644 --- a/src/bacnet/basic/object/timer.h +++ b/src/bacnet/basic/object/timer.h @@ -18,6 +18,21 @@ #include "bacnet/rp.h" #include "bacnet/list_element.h" +/** + * @brief Callback for tracking the timer writes for logging or other purposes + * @param instance - timer object instance number + * @param status - true if write was successful + * @param wp_data - pointer to the write property data structure + */ +typedef void (*timer_write_property_callback)( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); +/* linked list structure for notifications */ +struct timer_write_property_notification; +struct timer_write_property_notification { + struct timer_write_property_notification *next; + timer_write_property_callback callback; +}; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -166,6 +181,12 @@ void Timer_Task(uint32_t object_instance, uint16_t milliseconds); BACNET_STACK_EXPORT void Timer_Write_Property_Internal_Callback_Set(write_property_function cb); +BACNET_STACK_EXPORT +void Timer_Write_Property_Notification_Add( + struct timer_write_property_notification *notification); +BACNET_STACK_EXPORT +void Timer_Write_Property_Notify( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data); BACNET_STACK_EXPORT int Timer_Add_List_Element(BACNET_LIST_ELEMENT_DATA *list_element); diff --git a/test/bacnet/basic/object/channel/src/main.c b/test/bacnet/basic/object/channel/src/main.c index a2a5d169..ff815d2d 100644 --- a/test/bacnet/basic/object/channel/src/main.c +++ b/test/bacnet/basic/object/channel/src/main.c @@ -26,6 +26,20 @@ static bool Write_Property_Internal(BACNET_WRITE_PROPERTY_DATA *wp_data) return true; } +static struct channel_write_property_notification Write_Property_Notification; +static BACNET_WRITE_PROPERTY_DATA Write_Property_Notification_Data; +static uint32_t Write_Property_Notification_Instance; +static bool Write_Property_Notification_Status; +static void Channel_Write_Property_Notification_Callback( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + Write_Property_Notification_Instance = instance; + Write_Property_Notification_Status = status; + memcpy( + &Write_Property_Notification_Data, wp_data, + sizeof(BACNET_WRITE_PROPERTY_DATA)); +} + /** * @brief Test */ @@ -38,15 +52,21 @@ static void test_Channel_Property_Read_Write(void) const char *test_name = NULL; uint32_t test_instance = 0; bool status = false; + int len = 0; const int32_t skip_fail_property_list[] = { -1 }; BACNET_CHANNEL_VALUE channel_value = { 0 }; BACNET_WRITE_PROPERTY_DATA wp_data = { 0 }; BACNET_WRITE_GROUP_DATA wg_data = { 0 }; - BACNET_APPLICATION_DATA_VALUE value = { 0 }; + BACNET_APPLICATION_DATA_VALUE test_value = { 0 }; BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE member = { 0 }; Channel_Write_Property_Internal_Callback_Set(Write_Property_Internal); + Write_Property_Notification.callback = + Channel_Write_Property_Notification_Callback; + Write_Property_Notification.next = NULL; + Channel_Write_Property_Notification_Add(&Write_Property_Notification); + Channel_Init(); Channel_Create(instance); status = Channel_Valid_Instance(instance); @@ -162,6 +182,48 @@ static void test_Channel_Property_Read_Write(void) bacapp_encode_application_data(wp_data.application_data, &value); status = Channel_Write_Property(&wp_data); zassert_true(status, NULL); + /* does the callback object property match the last member? */ + zassert_equal( + Write_Property_Internal_Data.object_property, member.propertyIdentifier, + "%s:%d %s", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance, + bactext_property_name(Write_Property_Internal_Data.object_property)); + zassert_equal( + Write_Property_Internal_Data.object_type, member.objectIdentifier.type, + "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance); + zassert_equal( + Write_Property_Internal_Data.object_instance, + member.objectIdentifier.instance, "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance); + len = bacapp_decode_application_data( + Write_Property_Internal_Data.application_data, + Write_Property_Internal_Data.application_data_len, &test_value); + zassert_true(len > 0, "len=%d", len); + /* does the notify callback object property match the last member? */ + zassert_equal(Write_Property_Notification_Instance, instance, NULL); + zassert_equal(Write_Property_Notification_Status, true, NULL); + zassert_equal( + Write_Property_Notification_Data.object_property, + member.propertyIdentifier, NULL); + zassert_equal( + Write_Property_Notification_Data.object_type, + member.objectIdentifier.type, "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Notification_Data.object_type), + Write_Property_Notification_Data.object_instance); + zassert_equal( + Write_Property_Notification_Data.object_instance, + member.objectIdentifier.instance, "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Notification_Data.object_type), + Write_Property_Notification_Data.object_instance); + len = bacapp_decode_application_data( + Write_Property_Notification_Data.application_data, + Write_Property_Notification_Data.application_data_len, &test_value); + zassert_true(len > 0, "len=%d", len); + /* another coercion */ value.type.Channel_Value.tag = BACNET_APPLICATION_TAG_XY_COLOR; value.type.Channel_Value.type.XY_Color.x_coordinate = 0.4590f; value.type.Channel_Value.type.XY_Color.y_coordinate = 0.4101f; diff --git a/test/bacnet/basic/object/loop/src/main.c b/test/bacnet/basic/object/loop/src/main.c index 71bbc261..e05693e8 100644 --- a/test/bacnet/basic/object/loop/src/main.c +++ b/test/bacnet/basic/object/loop/src/main.c @@ -35,6 +35,20 @@ static int Read_Property_Internal(BACNET_READ_PROPERTY_DATA *data) return Read_Property_Internal_Length; } +static struct loop_write_property_notification Write_Property_Notification; +static BACNET_WRITE_PROPERTY_DATA Write_Property_Notification_Data; +static uint32_t Write_Property_Notification_Instance; +static bool Write_Property_Notification_Status; +static void Loop_Write_Property_Notification_Callback( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + Write_Property_Notification_Instance = instance; + Write_Property_Notification_Status = status; + memcpy( + &Write_Property_Notification_Data, wp_data, + sizeof(BACNET_WRITE_PROPERTY_DATA)); +} + static int Proprietary_Properties[] = { 512, 513, -1 }; static uint8_t Proprietary_Serial_Number[16]; @@ -369,6 +383,8 @@ static void test_Loop_Operation(void) bool status = false; uint32_t elapsed_time = 0; BACNET_OBJECT_PROPERTY_REFERENCE reference = { 0 }; + BACNET_APPLICATION_DATA_VALUE test_value = { 0 }; + int len = 0; /* init */ Loop_Init(); @@ -379,6 +395,10 @@ static void test_Loop_Operation(void) /* connect the read and write property callbacks */ Loop_Write_Property_Internal_Callback_Set(Write_Property_Internal); Loop_Read_Property_Internal_Callback_Set(Read_Property_Internal); + Write_Property_Notification.callback = + Loop_Write_Property_Notification_Callback; + Write_Property_Notification.next = NULL; + Loop_Write_Property_Notification_Add(&Write_Property_Notification); /* run the PID loop */ Loop_Timer(instance, elapsed_time); elapsed_time += 1000; @@ -402,6 +422,42 @@ static void test_Loop_Operation(void) Loop_Manipulated_Variable_Reference_Set(instance, &reference); elapsed_time += 100; Loop_Timer(instance, elapsed_time); + /* references - test by referencing another internal object */ + reference.object_identifier.type = OBJECT_ANALOG_OUTPUT; + reference.property_identifier = PROP_PRESENT_VALUE; + Loop_Manipulated_Variable_Reference_Set(instance, &reference); + elapsed_time += 100; + Loop_Timer(instance, elapsed_time); + /* verify that the internal read/write property callbacks were used */ + zassert_equal( + Write_Property_Internal_Data.object_type, OBJECT_ANALOG_OUTPUT, + "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance); + zassert_equal( + Write_Property_Internal_Data.object_instance, instance, + "WriteProperty=%s:%d", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance); + zassert_equal( + Write_Property_Internal_Data.object_property, PROP_PRESENT_VALUE, + "WriteProperty=%s:%d %s", + bactext_object_type_name(Write_Property_Internal_Data.object_type), + Write_Property_Internal_Data.object_instance, + bactext_property_name(Write_Property_Internal_Data.object_property)); + len = bacapp_decode_application_data( + Write_Property_Internal_Data.application_data, + Write_Property_Internal_Data.application_data_len, &test_value); + zassert_true(len > 0, "len=%d", len); + zassert_equal(Write_Property_Notification_Instance, instance, NULL); + zassert_equal(Write_Property_Notification_Status, true, NULL); + zassert_equal( + Write_Property_Notification_Data.object_property, PROP_PRESENT_VALUE, + NULL); + len = bacapp_decode_application_data( + Write_Property_Notification_Data.application_data, + Write_Property_Notification_Data.application_data_len, &test_value); + zassert_true(len > 0, "len=%d", len); /* cleanup instance */ status = Loop_Delete(instance); zassert_true(status, NULL); diff --git a/test/bacnet/basic/object/timer/src/main.c b/test/bacnet/basic/object/timer/src/main.c index d8396891..4e0db131 100644 --- a/test/bacnet/basic/object/timer/src/main.c +++ b/test/bacnet/basic/object/timer/src/main.c @@ -26,6 +26,20 @@ static bool Write_Property_Internal(BACNET_WRITE_PROPERTY_DATA *wp_data) return true; } +static struct timer_write_property_notification Write_Property_Notification; +static BACNET_WRITE_PROPERTY_DATA Write_Property_Notification_Data; +static uint32_t Write_Property_Notification_Instance; +static bool Write_Property_Notification_Status; +static void Timer_Write_Property_Notification_Callback( + uint32_t instance, bool status, BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + Write_Property_Notification_Instance = instance; + Write_Property_Notification_Status = status; + memcpy( + &Write_Property_Notification_Data, wp_data, + sizeof(BACNET_WRITE_PROPERTY_DATA)); +} + /** * @brief Test */ @@ -544,6 +558,15 @@ static void test_Timer_Operation_Transition_Default( Write_Property_Internal_Data.application_data, Write_Property_Internal_Data.application_data_len, &test_value); zassert_true(len > 0, "len=%d", len); + zassert_equal(Write_Property_Notification_Instance, instance, NULL); + zassert_equal(Write_Property_Notification_Status, true, NULL); + zassert_equal( + Write_Property_Notification_Data.object_property, PROP_PRESENT_VALUE, + NULL); + len = bacapp_decode_application_data( + Write_Property_Notification_Data.application_data, + Write_Property_Notification_Data.application_data_len, &test_value); + zassert_true(len > 0, "len=%d", len); value = Timer_State_Change_Value(instance, test_transition); zassert_equal(test_value.tag, value->tag, NULL); zassert_equal(test_value.type.Enumerated, value->type.Enumerated, NULL); @@ -576,6 +599,10 @@ static void test_Timer_Operation(void) datetime_timesync(&bdatetime.date, &bdatetime.time, false); /* configure the reference members and the write property values */ Timer_Write_Property_Internal_Callback_Set(Write_Property_Internal); + Write_Property_Notification.callback = + Timer_Write_Property_Notification_Callback; + Write_Property_Notification.next = NULL; + Timer_Write_Property_Notification_Add(&Write_Property_Notification); members = Timer_Reference_List_Member_Capacity(instance); for (i = 0; i < members; i++) { member.deviceIdentifier.type = OBJECT_DEVICE;