From 9a335b545eddb118d4b630dee2e68aee561b877e Mon Sep 17 00:00:00 2001 From: Ryan Mulder Date: Wed, 3 Dec 2025 17:05:26 -0500 Subject: [PATCH] Added `_Address` versions of a few services, primarily for interacting with MS/TP slaves (#1174) * add address versions of Send_Write_Property_Request and Send_Read_Property_Multiple_Request * add Send_COV_Subscribe_Address --- src/bacnet/basic/service/s_cov.c | 49 ++++++++--- src/bacnet/basic/service/s_cov.h | 5 ++ src/bacnet/basic/service/s_rpm.c | 55 +++++++++--- src/bacnet/basic/service/s_rpm.h | 7 ++ src/bacnet/basic/service/s_wp.c | 142 ++++++++++++++++++++++++------- src/bacnet/basic/service/s_wp.h | 21 +++++ 6 files changed, 221 insertions(+), 58 deletions(-) diff --git a/src/bacnet/basic/service/s_cov.c b/src/bacnet/basic/service/s_cov.c index 07b59780..0a9a5b21 100644 --- a/src/bacnet/basic/service/s_cov.c +++ b/src/bacnet/basic/service/s_cov.c @@ -90,19 +90,19 @@ int Send_UCOV_Notify( /** Sends a COV Subscription request. * @ingroup DSCOV * - * @param device_id [in] ID of the destination device + * @param dest [in] BACNET_ADDRESS of the destination device + * @param max_apdu [in] * @param cov_data [in] The COV subscription information to be encoded. * @return invoke id of outgoing message, or 0 if communication is disabled or * no slot is available from the tsm for sending. */ -uint8_t Send_COV_Subscribe( - uint32_t device_id, const BACNET_SUBSCRIBE_COV_DATA *cov_data) +uint8_t Send_COV_Subscribe_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + const BACNET_SUBSCRIBE_COV_DATA *cov_data) { - BACNET_ADDRESS dest; BACNET_ADDRESS my_address; - unsigned max_apdu = 0; uint8_t invoke_id = 0; - bool status = false; int len = 0; int pdu_len = 0; int bytes_sent = 0; @@ -111,18 +111,14 @@ uint8_t Send_COV_Subscribe( if (!dcc_communication_enabled()) { return 0; } - /* is the device bound? */ - status = address_get_by_device(device_id, &max_apdu, &dest); /* is there a tsm available? */ - if (status) { - invoke_id = tsm_next_free_invokeID(); - } + invoke_id = tsm_next_free_invokeID(); if (invoke_id) { /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); pdu_len = npdu_encode_pdu( - &Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data); + &Handler_Transmit_Buffer[0], dest, &my_address, &npdu_data); /* encode the APDU portion of the packet */ if (cov_data->covSubscribeToProperty) { /* subscribe to 1 property */ @@ -143,10 +139,10 @@ uint8_t Send_COV_Subscribe( max_apdu in the address binding table. */ if ((unsigned)pdu_len < max_apdu) { tsm_set_confirmed_unsegmented_transaction( - invoke_id, &dest, &npdu_data, &Handler_Transmit_Buffer[0], + invoke_id, dest, &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len); bytes_sent = datalink_send_pdu( - &dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); if (bytes_sent <= 0) { debug_perror("Failed to Send SubscribeCOV Request"); } @@ -162,3 +158,28 @@ uint8_t Send_COV_Subscribe( return invoke_id; } + +/** Sends a COV Subscription request. + * @ingroup DSCOV + * + * @param device_id [in] ID of the destination device + * @param cov_data [in] The COV subscription information to be encoded. + * @return invoke id of outgoing message, or 0 if communication is disabled or + * no slot is available from the tsm for sending. + */ +uint8_t Send_COV_Subscribe( + uint32_t device_id, const BACNET_SUBSCRIBE_COV_DATA *cov_data) +{ + BACNET_ADDRESS dest = { 0 }; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + if (status) { + invoke_id = Send_COV_Subscribe_Address(&dest, max_apdu, cov_data); + } + + return invoke_id; +} diff --git a/src/bacnet/basic/service/s_cov.h b/src/bacnet/basic/service/s_cov.h index 7f5528a8..636e7fe1 100644 --- a/src/bacnet/basic/service/s_cov.h +++ b/src/bacnet/basic/service/s_cov.h @@ -39,6 +39,11 @@ int ucov_notify_encode_pdu( BACNET_NPDU_DATA *npdu_data, const BACNET_COV_DATA *cov_data); BACNET_STACK_EXPORT +uint8_t Send_COV_Subscribe_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + const BACNET_SUBSCRIBE_COV_DATA *cov_data); +BACNET_STACK_EXPORT uint8_t Send_COV_Subscribe( uint32_t device_id, const BACNET_SUBSCRIBE_COV_DATA *cov_data); diff --git a/src/bacnet/basic/service/s_rpm.c b/src/bacnet/basic/service/s_rpm.c index a6dd2449..85d24217 100644 --- a/src/bacnet/basic/service/s_rpm.c +++ b/src/bacnet/basic/service/s_rpm.c @@ -27,6 +27,8 @@ /** * @brief Sends a Read Property Multiple request. * @ingroup BIBB-DS-RPM-A + * @param dest [in] BACNET_ADDRESS of the destination device + * @param max_apdu [in] * @param pdu [out] Buffer to build the outgoing message into * @param max_pdu [in] Length of the pdu buffer. * @param device_id [in] ID of the destination device @@ -35,17 +37,16 @@ * @return invoke id of outgoing message, or 0 if device is not bound or no tsm * available */ -uint8_t Send_Read_Property_Multiple_Request( +BACNET_STACK_EXPORT +uint8_t Send_Read_Property_Multiple_Request_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, uint8_t *pdu, size_t max_pdu, - uint32_t device_id, /* destination device */ BACNET_READ_ACCESS_DATA *read_access_data) { - BACNET_ADDRESS dest; BACNET_ADDRESS my_address; - unsigned max_apdu = 0; uint8_t invoke_id = 0; - bool status = false; int len = 0; int pdu_len = 0; int bytes_sent = 0; @@ -54,17 +55,12 @@ uint8_t Send_Read_Property_Multiple_Request( if (!dcc_communication_enabled()) { return 0; } - /* is the device bound? */ - status = address_get_by_device(device_id, &max_apdu, &dest); - /* is there a tsm available? */ - if (status) { - invoke_id = tsm_next_free_invokeID(); - } + invoke_id = tsm_next_free_invokeID(); if (invoke_id) { /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); - pdu_len = npdu_encode_pdu(&pdu[0], &dest, &my_address, &npdu_data); + pdu_len = npdu_encode_pdu(&pdu[0], dest, &my_address, &npdu_data); /* encode the APDU portion of the packet */ len = rpm_encode_apdu( &pdu[pdu_len], max_pdu - pdu_len, invoke_id, read_access_data); @@ -79,8 +75,8 @@ uint8_t Send_Read_Property_Multiple_Request( max_apdu in the address binding table. */ if ((unsigned)pdu_len < max_apdu) { tsm_set_confirmed_unsegmented_transaction( - invoke_id, &dest, &npdu_data, &pdu[0], (uint16_t)pdu_len); - bytes_sent = datalink_send_pdu(&dest, &npdu_data, &pdu[0], pdu_len); + invoke_id, dest, &npdu_data, &pdu[0], (uint16_t)pdu_len); + bytes_sent = datalink_send_pdu(dest, &npdu_data, &pdu[0], pdu_len); if (bytes_sent <= 0) { debug_perror("Failed to Send ReadPropertyMultiple Request"); } @@ -96,3 +92,34 @@ uint8_t Send_Read_Property_Multiple_Request( return invoke_id; } + +/** + * @brief Sends a Read Property Multiple request. + * @ingroup BIBB-DS-RPM-A + * @param pdu [out] Buffer to build the outgoing message into + * @param max_pdu [in] Length of the pdu buffer. + * @param device_id [in] ID of the destination device + * @param read_access_data [in] Ptr to structure with the linked list of + * properties to be read. + * @return invoke id of outgoing message, or 0 if device is not bound or no tsm + * available + */ +uint8_t Send_Read_Property_Multiple_Request( + uint8_t *pdu, + size_t max_pdu, + uint32_t device_id, /* destination device */ + BACNET_READ_ACCESS_DATA *read_access_data) +{ + BACNET_ADDRESS dest = { 0 }; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + if (status) { + invoke_id = Send_Read_Property_Multiple_Request_Address( + &dest, max_apdu, pdu, max_pdu, read_access_data); + } + return invoke_id; +} diff --git a/src/bacnet/basic/service/s_rpm.h b/src/bacnet/basic/service/s_rpm.h index 4fc8b420..611b8d95 100644 --- a/src/bacnet/basic/service/s_rpm.h +++ b/src/bacnet/basic/service/s_rpm.h @@ -26,6 +26,13 @@ extern "C" { #endif /* __cplusplus */ +BACNET_STACK_EXPORT +uint8_t Send_Read_Property_Multiple_Request_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + uint8_t *pdu, + size_t max_pdu, + BACNET_READ_ACCESS_DATA *read_access_data); BACNET_STACK_EXPORT uint8_t Send_Read_Property_Multiple_Request( uint8_t *pdu, diff --git a/src/bacnet/basic/service/s_wp.c b/src/bacnet/basic/service/s_wp.c index b0fb36f3..be653880 100644 --- a/src/bacnet/basic/service/s_wp.c +++ b/src/bacnet/basic/service/s_wp.c @@ -31,8 +31,9 @@ * @param data [in] The data representing the Life Safety Operation * @return invoke id of outgoing message, or zero on failure. */ -uint8_t Send_Write_Property_Request_Data( - uint32_t device_id, +uint8_t Send_Write_Property_Request_Data_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, BACNET_OBJECT_TYPE object_type, uint32_t object_instance, BACNET_PROPERTY_ID object_property, @@ -41,11 +42,8 @@ uint8_t Send_Write_Property_Request_Data( uint8_t priority, uint32_t array_index) { - BACNET_ADDRESS dest; BACNET_ADDRESS my_address; - unsigned max_apdu = 0; uint8_t invoke_id = 0; - bool status = false; int len = 0; int pdu_len = 0; int bytes_sent = 0; @@ -55,18 +53,14 @@ uint8_t Send_Write_Property_Request_Data( if (!dcc_communication_enabled()) { return 0; } - /* is the device bound? */ - status = address_get_by_device(device_id, &max_apdu, &dest); - /* is there a tsm available? */ - if (status) { - invoke_id = tsm_next_free_invokeID(); - } + + invoke_id = tsm_next_free_invokeID(); if (invoke_id) { /* encode the NPDU portion of the packet */ datalink_get_my_address(&my_address); npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL); pdu_len = npdu_encode_pdu( - &Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data); + &Handler_Transmit_Buffer[0], dest, &my_address, &npdu_data); /* encode the APDU portion of the packet */ data.object_type = object_type; data.object_instance = object_instance; @@ -87,10 +81,10 @@ uint8_t Send_Write_Property_Request_Data( max_apdu in the address binding table. */ if ((unsigned)pdu_len < max_apdu) { tsm_set_confirmed_unsegmented_transaction( - invoke_id, &dest, &npdu_data, &Handler_Transmit_Buffer[0], + invoke_id, dest, &npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len); bytes_sent = datalink_send_pdu( - &dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); + dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len); if (bytes_sent <= 0) { debug_perror("Failed to Send WriteProperty Request"); } @@ -106,6 +100,107 @@ uint8_t Send_Write_Property_Request_Data( return invoke_id; } +/** + * @brief Send a WriteProperty-Request service message + * @ingroup BIBB-DS-WP-A + * @param device_id [in] ID of the destination device + * @param data [in] The data representing the Life Safety Operation + * @return invoke id of outgoing message, or zero on failure. + */ +uint8_t Send_Write_Property_Request_Data( + uint32_t device_id, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + const uint8_t *application_data, + int application_data_len, + uint8_t priority, + uint32_t array_index) +{ + BACNET_ADDRESS dest = { 0 }; + unsigned max_apdu = 0; + uint8_t invoke_id = 0; + bool status = false; + + /* is the device bound? */ + status = address_get_by_device(device_id, &max_apdu, &dest); + if (status) { + invoke_id = Send_Write_Property_Request_Data_Address( + &dest, max_apdu, object_type, object_instance, object_property, + application_data, application_data_len, priority, array_index); + } + + return invoke_id; +} + +/** + * @brief Encode the object_value and store it into application_data. + * @param application_data Buffer to store the encoded data + * only + * @param object_value Pointer to the application value structure + * @return Length of the encoded data in bytes, or 0 if it does not fit in + * application_data + */ +static int encode_object_value( + const BACNET_APPLICATION_DATA_VALUE *object_value, + uint8_t *application_data, + const int application_data_len) +{ + int apdu_len = 0, len = 0; + while (object_value) { + debug_printf( + "WriteProperty service: " + "%s tag=%d\n", + (object_value->context_specific ? "context" : "application"), + (int)(object_value->context_specific ? object_value->context_tag + : object_value->tag)); + len = bacapp_encode_data(&application_data[apdu_len], object_value); + if ((len + apdu_len) < application_data_len) { + apdu_len += len; + } else { + return 0; + } + object_value = object_value->next; + } + return apdu_len; +} + +/** + * @brief Sends a Write Property request. + * @ingroup BIBB-DS-WP-A + * @param device_id [in] ID of the destination device + * @param object_type [in] Type of the object whose property is to be written. + * @param object_instance [in] Instance # of the object to be written. + * @param object_property [in] Property to be written. + * @param object_value [in] The value to be written to the property. + * @param priority [in] Write priority of 1 (highest) to 16 (lowest) + * @param array_index [in] Optional: if the Property is an array, + * - 0 for the array size + * - 1 to n for individual array members + * - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent) + * @return invoke id of outgoing message, or 0 on failure. + */ +BACNET_STACK_EXPORT +uint8_t Send_Write_Property_Request_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + const BACNET_APPLICATION_DATA_VALUE *object_value, + uint8_t priority, + uint32_t array_index) +{ + uint8_t application_data[MAX_APDU] = { 0 }; + int apdu_len = 0; + + apdu_len = encode_object_value( + object_value, application_data, sizeof application_data); + + return Send_Write_Property_Request_Data_Address( + dest, max_apdu, object_type, object_instance, object_property, + &application_data[0], apdu_len, priority, array_index); +} /** * @brief Sends a Write Property request. @@ -132,23 +227,10 @@ uint8_t Send_Write_Property_Request( uint32_t array_index) { uint8_t application_data[MAX_APDU] = { 0 }; - int apdu_len = 0, len = 0; + int apdu_len = 0; - while (object_value) { - debug_printf( - "WriteProperty service: " - "%s tag=%d\n", - (object_value->context_specific ? "context" : "application"), - (int)(object_value->context_specific ? object_value->context_tag - : object_value->tag)); - len = bacapp_encode_data(&application_data[apdu_len], object_value); - if ((len + apdu_len) < MAX_APDU) { - apdu_len += len; - } else { - return 0; - } - object_value = object_value->next; - } + apdu_len = encode_object_value( + object_value, application_data, sizeof application_data); return Send_Write_Property_Request_Data( device_id, object_type, object_instance, object_property, diff --git a/src/bacnet/basic/service/s_wp.h b/src/bacnet/basic/service/s_wp.h index 868c627b..904bc186 100644 --- a/src/bacnet/basic/service/s_wp.h +++ b/src/bacnet/basic/service/s_wp.h @@ -25,6 +25,16 @@ extern "C" { #endif /* __cplusplus */ +BACNET_STACK_EXPORT +uint8_t Send_Write_Property_Request_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + const BACNET_APPLICATION_DATA_VALUE *object_value, + uint8_t priority, + uint32_t array_index); BACNET_STACK_EXPORT uint8_t Send_Write_Property_Request( uint32_t device_id, /* destination device */ @@ -35,6 +45,17 @@ uint8_t Send_Write_Property_Request( uint8_t priority, uint32_t array_index); BACNET_STACK_EXPORT +uint8_t Send_Write_Property_Request_Data_Address( + BACNET_ADDRESS *dest, + uint16_t max_apdu, + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + const uint8_t *application_data, + int application_data_len, + uint8_t priority, + uint32_t array_index); +BACNET_STACK_EXPORT uint8_t Send_Write_Property_Request_Data( uint32_t device_id, BACNET_OBJECT_TYPE object_type,