From 1444715c5a5a318c5aa8cde55752862e4d89c3c1 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 29 Oct 2024 14:28:21 -0500 Subject: [PATCH] Add network port callbacks for pending changes activate and discard. (#836) --- .../basic/object/client/device-client.c | 121 ++++++++++++++++++ src/bacnet/basic/object/device.c | 16 +++ src/bacnet/basic/object/netport.c | 45 ++++++- src/bacnet/basic/object/netport.h | 20 +++ 4 files changed, 200 insertions(+), 2 deletions(-) diff --git a/src/bacnet/basic/object/client/device-client.c b/src/bacnet/basic/object/client/device-client.c index df5311fc..75698c6e 100644 --- a/src/bacnet/basic/object/client/device-client.c +++ b/src/bacnet/basic/object/client/device-client.c @@ -25,6 +25,9 @@ #include "bacnet/basic/services.h" #include "bacnet/datalink/datalink.h" #include "bacnet/basic/binding/address.h" +#if (BACFILE) +#include "bacnet/basic/object/bacfile.h" +#endif #if (BACNET_PROTOCOL_REVISION >= 17) #include "bacnet/basic/object/netport.h" #endif @@ -1172,6 +1175,124 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata) return apdu_len; } +/** + * @brief Handles the writing of the object name property + * @param wp_data [in,out] WriteProperty data structure + * @param Object_Write_Property object specific function to write the property + * @return True on success, else False if there is an error. + */ +static bool Device_Write_Property_Object_Name( + BACNET_WRITE_PROPERTY_DATA *wp_data, + write_property_function Object_Write_Property) +{ + bool status = false; /* return value */ + int len = 0; + BACNET_CHARACTER_STRING value; + BACNET_OBJECT_TYPE object_type = OBJECT_NONE; + uint32_t object_instance = 0; + int apdu_size = 0; + uint8_t *apdu = NULL; + + if (!wp_data) { + return false; + } + if (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; + } + apdu = wp_data->application_data; + apdu_size = wp_data->application_data_len; + len = bacnet_character_string_application_decode(apdu, apdu_size, &value); + if (len > 0) { + if ((characterstring_encoding(&value) != CHARACTER_ANSI_X34) || + (characterstring_length(&value) == 0) || + (!characterstring_printable(&value))) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } else { + status = true; + } + } else if (len == 0) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE; + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE; + } + if (status) { + /* All the object names in a device must be unique */ + if (Device_Valid_Object_Name(&value, &object_type, &object_instance)) { + if ((object_type == wp_data->object_type) && + (object_instance == wp_data->object_instance)) { + /* writing same name to same object */ + status = true; + } else { + /* name already exists in some object */ + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_DUPLICATE_NAME; + status = false; + } + } else { + status = Object_Write_Property(wp_data); + } + } + + return status; +} + +/** Looks up the requested Object and Property, and set the new Value in it, + * if allowed. + * If the Object or Property can't be found, sets the error class and code. + * @ingroup ObjIntf + * + * @param wp_data [in,out] Structure with the desired Object and Property info + * and new Value on entry, and APDU message on return. + * @return True on success, else False if there is an error. + */ +bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data) +{ + bool status = false; /* Ever the pessimist! */ + struct object_functions *pObject = NULL; + + /* initialize the default return values */ + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + pObject = Device_Objects_Find_Functions(wp_data->object_type); + if (pObject != NULL) { + if (pObject->Object_Valid_Instance && + pObject->Object_Valid_Instance(wp_data->object_instance)) { + if (pObject->Object_Write_Property) { +#if (BACNET_PROTOCOL_REVISION >= 14) + if (wp_data->object_property == PROP_PROPERTY_LIST) { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + return status; + } +#endif + if (wp_data->object_property == PROP_OBJECT_NAME) { + status = Device_Write_Property_Object_Name( + wp_data, pObject->Object_Write_Property); + } else { + status = pObject->Object_Write_Property(wp_data); + } + } else { + wp_data->error_class = ERROR_CLASS_PROPERTY; + wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + } else { + wp_data->error_class = ERROR_CLASS_OBJECT; + wp_data->error_code = ERROR_CODE_UNKNOWN_OBJECT; + } + + return (status); +} + /** * @brief Updates all the object timers with elapsed milliseconds * @param milliseconds - number of milliseconds elapsed diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index 60f79eba..3fd6abac 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -666,6 +666,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) { bool status = false; bool password_success = false; + unsigned i; /* From 16.4.1.1.2 Password This optional parameter shall be a CharacterString of up to @@ -690,8 +691,19 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) if (password_success) { switch (rd_data->state) { case BACNET_REINIT_COLDSTART: + dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + /* note: you probably want to restart *after* the + simple ack has been sent from the return handler + so just set a flag from here */ + Reinitialize_State = rd_data->state; + status = true; + break; case BACNET_REINIT_WARMSTART: dcc_set_status_duration(COMMUNICATION_ENABLE, 0); + for (i = 0; i < Network_Port_Count(); i++) { + Network_Port_Changes_Pending_Activate( + Network_Port_Index_To_Instance(i)); + } /* note: you probably want to restart *after* the simple ack has been sent from the return handler so just set a flag from here */ @@ -714,6 +726,10 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) break; case BACNET_REINIT_ACTIVATE_CHANGES: /* note: activate changes *after* the simple ack is sent */ + for (i = 0; i < Network_Port_Count(); i++) { + Network_Port_Changes_Pending_Activate( + Network_Port_Index_To_Instance(i)); + } Reinitialize_State = rd_data->state; status = true; break; diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index 1a6124aa..a1401590 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -104,6 +104,8 @@ struct object_data { BACNET_PORT_QUALITY Quality; uint16_t APDU_Length; float Link_Speed; + bacnet_network_port_activate_changes Activate_Changes; + bacnet_network_port_discard_changes Discard_Changes; union { struct bacnet_ipv4_port IPv4; struct bacnet_ipv6_port IPv6; @@ -956,6 +958,9 @@ bool Network_Port_Changes_Pending_Set(uint32_t object_instance, bool value) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { Object_List[index].Changes_Pending = value; + if (value == false) { + Network_Port_Changes_Pending_Discard(object_instance); + } status = true; } @@ -972,7 +977,25 @@ void Network_Port_Changes_Pending_Activate(uint32_t object_instance) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { - /* callback? something else? */ + if (Object_List[index].Activate_Changes) { + Object_List[index].Activate_Changes(object_instance); + } + } +} + +/** + * @brief For a given object instance-number, sets the callback function + * to activate any pending changes + */ +void Network_Port_Changes_Pending_Activate_Callback_Set( + uint32_t object_instance, bacnet_network_port_activate_changes callback) + +{ + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + Object_List[index].Activate_Changes = callback; } } @@ -986,7 +1009,25 @@ void Network_Port_Changes_Pending_Discard(uint32_t object_instance) index = Network_Port_Instance_To_Index(object_instance); if (index < BACNET_NETWORK_PORTS_MAX) { - /* callback? something else? */ + if (Object_List[index].Discard_Changes) { + Object_List[index].Discard_Changes(object_instance); + } + } +} + +/** + * @brief For a given object instance-number, sets the callback function + * to discard any pending changes + */ +void Network_Port_Changes_Pending_Discard_Callback_Set( + uint32_t object_instance, bacnet_network_port_discard_changes callback) + +{ + unsigned index = 0; + + index = Network_Port_Instance_To_Index(object_instance); + if (index < BACNET_NETWORK_PORTS_MAX) { + Object_List[index].Discard_Changes = callback; } } diff --git a/src/bacnet/basic/object/netport.h b/src/bacnet/basic/object/netport.h index 60ac84da..c4917ce7 100644 --- a/src/bacnet/basic/object/netport.h +++ b/src/bacnet/basic/object/netport.h @@ -17,6 +17,20 @@ #include "bacnet/rp.h" #include "bacnet/wp.h" +/** + * @brief API for a network port object when changes need to be activated + * @param object_instance [in] Object instance number + * @return true if successful, else false + */ +typedef void (*bacnet_network_port_activate_changes)(uint32_t object_instance); + +/** + * @brief API for a network port object when changes need to be discarded + * @param object_instance [in] Object instance number + * @return true if successful, else false + */ +typedef void (*bacnet_network_port_discard_changes)(uint32_t object_instance); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -318,7 +332,13 @@ bool Network_Port_Changes_Pending_Set(uint32_t instance, bool flag); BACNET_STACK_EXPORT void Network_Port_Changes_Pending_Activate(uint32_t instance); BACNET_STACK_EXPORT +void Network_Port_Changes_Pending_Activate_Callback_Set( + uint32_t instance, bacnet_network_port_activate_changes callback); +BACNET_STACK_EXPORT void Network_Port_Changes_Pending_Discard(uint32_t instance); +BACNET_STACK_EXPORT +void Network_Port_Changes_Pending_Discard_Callback_Set( + uint32_t instance, bacnet_network_port_discard_changes callback); BACNET_STACK_EXPORT bool Network_Port_Valid_Instance(uint32_t object_instance);