Add segmentation support functions (#1218)

* Added segmentation support functions and example changes, but no support for segmentation in the TSM or APDU handlers.
This commit is contained in:
Steve Karg
2026-02-04 17:03:13 -06:00
committed by GitHub
parent 577ecf8d7f
commit 8cd1d82d81
14 changed files with 224 additions and 4 deletions
+1
View File
@@ -23,6 +23,7 @@ Frédéric Chaxel <fchaxel@users.sourceforge.net> <fchaxel@1e814d3a-8a88-4260-be
Miroslav Novotny <koby3101@users.sourceforge.net> <koby3101@1e814d3a-8a88-4260-be86-c0dc25669c1e> Miroslav Novotny <koby3101@users.sourceforge.net> <koby3101@1e814d3a-8a88-4260-be86-c0dc25669c1e>
Boris Weitsch <bow2@users.sourceforge.net> <bow2@1e814d3a-8a88-4260-be86-c0dc25669c1e> Boris Weitsch <bow2@users.sourceforge.net> <bow2@1e814d3a-8a88-4260-be86-c0dc25669c1e>
Vasyl Tkhir <vasyl-tkhir@users.sourceforge.net> <vasyl-tkhir@1e814d3a-8a88-4260-be86-c0dc25669c1e> Vasyl Tkhir <vasyl-tkhir@users.sourceforge.net> <vasyl-tkhir@1e814d3a-8a88-4260-be86-c0dc25669c1e>
Julien Bennet <antibarbie@users.sourceforge.net> <antibarbie@1e814d3a-8a88-4260-be86-c0dc25669c1e>
# github mappings # github mappings
Greg Shue <greg.shue@outlook.com> <32416235+shuegr-personal@users.noreply.github.com> Greg Shue <greg.shue@outlook.com> <32416235+shuegr-personal@users.noreply.github.com>
+2
View File
@@ -32,6 +32,8 @@ The git repositories are hosted at the following sites:
### Added ### Added
* Added segmentation support functions and example changes, but
no support for segmentation in the TSM or APDU handlers. (#1218)
* Added channel and timer object write-property observers in blinkt app * Added channel and timer object write-property observers in blinkt app
to monitor internal writes. Added vacancy timer command line argument to monitor internal writes. Added vacancy timer command line argument
for testing initial timer object vacancy time for lights channel. (#1212) for testing initial timer object vacancy time for lights channel. (#1212)
+9
View File
@@ -84,6 +84,11 @@ option(
"compile with secure-connect support" "compile with secure-connect support"
OFF) OFF)
option(
BACNET_SEGMENTATION_ENABLED
"enable segmentation"
ON)
if(NOT (BACDL_ETHERNET OR if(NOT (BACDL_ETHERNET OR
BACDL_MSTP OR BACDL_MSTP OR
BACDL_ARCNET OR BACDL_ARCNET OR
@@ -683,6 +688,8 @@ add_library(${PROJECT_NAME}
src/bacnet/rp.h src/bacnet/rp.h
src/bacnet/rpm.c src/bacnet/rpm.c
src/bacnet/rpm.h src/bacnet/rpm.h
$<$<BOOL:${BACNET_SEGMENTATION_ENABLED}>:src/bacnet/segmentack.c>
$<$<BOOL:${BACNET_SEGMENTATION_ENABLED}>:src/bacnet/segmentack.h>
src/bacnet/shed_level.c src/bacnet/shed_level.c
src/bacnet/shed_level.h src/bacnet/shed_level.h
src/bacnet/timer_value.c src/bacnet/timer_value.c
@@ -736,6 +743,7 @@ target_compile_definitions(
$<$<BOOL:${BACDL_NONE}>:BACDL_NONE> $<$<BOOL:${BACDL_NONE}>:BACDL_NONE>
$<$<BOOL:${BACNET_PROPERTY_LISTS}>:BACNET_PROPERTY_LISTS=1> $<$<BOOL:${BACNET_PROPERTY_LISTS}>:BACNET_PROPERTY_LISTS=1>
$<$<BOOL:${BAC_ROUTING}>:BAC_ROUTING> $<$<BOOL:${BAC_ROUTING}>:BAC_ROUTING>
$<$<BOOL:${BACNET_SEGMENTATION_ENABLED}>:BACNET_SEGMENTATION_ENABLED=1>
$<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BACNET_STACK_STATIC_DEFINE> $<$<NOT:$<BOOL:${BUILD_SHARED_LIBS}>>:BACNET_STACK_STATIC_DEFINE>
PRIVATE PRIVATE
PRINT_ENABLED=1) PRINT_ENABLED=1)
@@ -1302,3 +1310,4 @@ message(STATUS "BACNET: BACDL_ARCNET:...................\"${BACDL_ARCNET}\"")
message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"") message(STATUS "BACNET: BACDL_MSTP:.....................\"${BACDL_MSTP}\"")
message(STATUS "BACNET: BACDL_ZIGBEE:...................\"${BACDL_ZIGBEE}\"") message(STATUS "BACNET: BACDL_ZIGBEE:...................\"${BACDL_ZIGBEE}\"")
message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"") message(STATUS "BACNET: BACDL_ETHERNET:.................\"${BACDL_ETHERNET}\"")
message(STATUS "BACNET: BACNET_SEGMENTATION_ENABLED:....\"${BACNET_SEGMENTATION_ENABLED}\"")
+4
View File
@@ -224,6 +224,10 @@ server-discover:
server-mini: server-mini:
$(MAKE) LEGACY=true NOTIFY=false -s -C apps $@ $(MAKE) LEGACY=true NOTIFY=false -s -C apps $@
.PHONY: server-segmentation
server-segmentation:
$(MAKE) LEGACY=true SEGMENT=true -s -C apps server
.PHONY: sc-hub .PHONY: sc-hub
sc-hub: sc-hub:
$(MAKE) LEGACY=true BACDL=bsc -s -C apps $@ $(MAKE) LEGACY=true BACDL=bsc -s -C apps $@
+5
View File
@@ -223,6 +223,11 @@ ifeq (${LEGACY},true)
BACNET_DEFINES += -DBACNET_STACK_DEPRECATED_DISABLE BACNET_DEFINES += -DBACNET_STACK_DEPRECATED_DISABLE
endif endif
ifeq (${SEGMENT},true)
# enable segmentation support
BACNET_DEFINES += -DBACNET_SEGMENTATION_ENABLED=1
endif
ifeq (${NOTIFY},false) ifeq (${NOTIFY},false)
# disable intrinsic reporting # disable intrinsic reporting
else else
+5
View File
@@ -58,6 +58,11 @@ bool Device_Set_Object_Instance_Number(uint32_t object_id)
return status; return status;
} }
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
{
return SEGMENTATION_NONE;
}
bool Device_Valid_Object_Instance_Number(uint32_t object_id) bool Device_Valid_Object_Instance_Number(uint32_t object_id)
{ {
/* BACnet allows for a wildcard instance number */ /* BACnet allows for a wildcard instance number */
+16
View File
@@ -212,8 +212,24 @@ typedef struct BACnet_Object_Id {
uint32_t instance; uint32_t instance;
} BACNET_OBJECT_ID; } BACNET_OBJECT_ID;
#if !defined(BACNET_MAX_SEGMENTS_ACCEPTED)
#if BACNET_SEGMENTATION_ENABLED
/* note: BACNET_MAX_SEGMENTS_ACCEPTED can be 1..255.
ASDU in this library is usually sized for 16-bit at 65535 max.
Therefore, the default here is limited to avoid overflow warnings. */
#define BACNET_MAX_SEGMENTS_ACCEPTED 32
#else
#define BACNET_MAX_SEGMENTS_ACCEPTED 1
#endif
#endif
#if !defined(MAX_APDU)
#define MAX_APDU 1476
#endif
#define MAX_NPDU (1 + 1 + 2 + 1 + MAX_MAC_LEN + 2 + 1 + MAX_MAC_LEN + 1 + 1 + 2) #define MAX_NPDU (1 + 1 + 2 + 1 + MAX_MAC_LEN + 2 + 1 + MAX_MAC_LEN + 1 + 1 + 2)
#define MAX_PDU (MAX_APDU + MAX_NPDU) #define MAX_PDU (MAX_APDU + MAX_NPDU)
/* Application Service Data Unit (ASDU) that has not yet been segmented
into a protocol data unit (PDU) by the lower layer. */
#define MAX_ASDU ((MAX_APDU * BACNET_MAX_SEGMENTS_ACCEPTED) + MAX_NPDU)
#define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) \ #define BACNET_ID_VALUE(bacnet_object_instance, bacnet_object_type) \
((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | \ ((((bacnet_object_type) & BACNET_MAX_OBJECT) << BACNET_INSTANCE_BITS) | \
+39 -2
View File
@@ -45,6 +45,10 @@ static struct Address_Cache_Entry {
uint8_t Flags; uint8_t Flags;
uint32_t device_id; uint32_t device_id;
unsigned max_apdu; unsigned max_apdu;
#if BACNET_SEGMENTATION_ENABLED
uint8_t segmentation;
uint16_t maxsegments;
#endif
BACNET_ADDRESS address; BACNET_ADDRESS address;
uint32_t TimeToLive; uint32_t TimeToLive;
} Address_Cache[MAX_ADDRESS_CACHE]; } Address_Cache[MAX_ADDRESS_CACHE];
@@ -346,9 +350,16 @@ void address_set_device_TTL(
* @param device_id Device-Id * @param device_id Device-Id
* @param max_apdu Pointer to a variable, taking the maximum APDU size. * @param max_apdu Pointer to a variable, taking the maximum APDU size.
* @param src Pointer to address structure for return. * @param src Pointer to address structure for return.
* @param segmentation Pointer to a variable, taking the BACNET_SEGMENTATION
* flag.
* @param maxsegments Pointer to a variable, taking the maximum segments.
*/ */
bool address_get_by_device( bool address_segment_get_by_device(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src) uint32_t device_id,
unsigned *max_apdu,
BACNET_ADDRESS *src,
uint8_t *segmentation,
uint16_t *maxsegments)
{ {
struct Address_Cache_Entry *pMatch; struct Address_Cache_Entry *pMatch;
bool found = false; /* return value */ bool found = false; /* return value */
@@ -364,6 +375,20 @@ bool address_get_by_device(
if (max_apdu) { if (max_apdu) {
*max_apdu = pMatch->max_apdu; *max_apdu = pMatch->max_apdu;
} }
if (segmentation) {
#if BACNET_SEGMENTATION_ENABLED
*segmentation = pMatch->segmentation;
#else
*segmentation = SEGMENTATION_NONE;
#endif
}
if (maxsegments) {
#if BACNET_SEGMENTATION_ENABLED
*maxsegments = pMatch->maxsegments;
#else
*maxsegments = 1;
#endif
}
/* Prove we found it */ /* Prove we found it */
found = true; found = true;
} }
@@ -375,6 +400,18 @@ bool address_get_by_device(
return found; return found;
} }
/**
* @brief Return the cached address for the given device-id
* @param device_id Device-Id
* @param max_apdu Pointer to a variable, taking the maximum APDU size.
* @param src Pointer to address structure for return.
*/
bool address_get_by_device(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src)
{
return address_segment_get_by_device(device_id, max_apdu, src, NULL, NULL);
}
/** /**
* Find a device id from a given MAC address. * Find a device id from a given MAC address.
* *
+8
View File
@@ -43,6 +43,14 @@ BACNET_STACK_EXPORT
bool address_get_by_device( bool address_get_by_device(
uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src); uint32_t device_id, unsigned *max_apdu, BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool address_segment_get_by_device(
uint32_t device_id,
unsigned *max_apdu,
BACNET_ADDRESS *src,
uint8_t *segmentation,
uint16_t *maxsegments);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
bool address_get_by_index( bool address_get_by_index(
unsigned index, unsigned index,
+8
View File
@@ -1333,7 +1333,11 @@ uint8_t Device_Protocol_Revision(void)
BACNET_SEGMENTATION Device_Segmentation_Supported(void) BACNET_SEGMENTATION Device_Segmentation_Supported(void)
{ {
#if BACNET_SEGMENTATION_ENABLED
return SEGMENTATION_BOTH;
#else
return SEGMENTATION_NONE; return SEGMENTATION_NONE;
#endif
} }
/** /**
@@ -1804,8 +1808,12 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
rpdata->object_instance, rpdata->array_index, rpdata->object_instance, rpdata->array_index,
Device_Object_List_Element_Encode, count, apdu, apdu_max); Device_Object_List_Element_Encode, count, apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) { if (apdu_len == BACNET_STATUS_ABORT) {
#if BACNET_SEGMENTATION_ENABLED
rpdata->error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW;
#else
rpdata->error_code = rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED; ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
#endif
} else if (apdu_len == BACNET_STATUS_ERROR) { } else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY; rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX; rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
+2 -2
View File
@@ -85,7 +85,7 @@ int iam_encode_pdu(
/* encode the APDU portion of the packet */ /* encode the APDU portion of the packet */
len = iam_encode_apdu( len = iam_encode_apdu(
&buffer[pdu_len], Device_Object_Instance_Number(), MAX_APDU, &buffer[pdu_len], Device_Object_Instance_Number(), MAX_APDU,
SEGMENTATION_NONE, Device_Vendor_Identifier()); Device_Segmentation_Supported(), Device_Vendor_Identifier());
pdu_len += len; pdu_len += len;
return pdu_len; return pdu_len;
@@ -158,7 +158,7 @@ int iam_unicast_encode_pdu(
/* encode the APDU portion of the packet */ /* encode the APDU portion of the packet */
apdu_len = iam_encode_apdu( apdu_len = iam_encode_apdu(
&buffer[npdu_len], Device_Object_Instance_Number(), MAX_APDU, &buffer[npdu_len], Device_Object_Instance_Number(), MAX_APDU,
SEGMENTATION_NONE, Device_Vendor_Identifier()); Device_Segmentation_Supported(), Device_Vendor_Identifier());
pdu_len = npdu_len + apdu_len; pdu_len = npdu_len + apdu_len;
return pdu_len; return pdu_len;
+5
View File
@@ -171,6 +171,11 @@
#endif #endif
#endif #endif
/* Enable or disable segmentation support in the library */
#ifndef BACNET_SEGMENTATION_ENABLED
#define BACNET_SEGMENTATION_ENABLED 0
#endif
/* for confirmed messages, this is the number of transactions */ /* for confirmed messages, this is the number of transactions */
/* that we hold in a queue waiting for timeout. */ /* that we hold in a queue waiting for timeout. */
/* Configure to zero if you don't want any confirmed messages */ /* Configure to zero if you don't want any confirmed messages */
+79
View File
@@ -0,0 +1,79 @@
/**
* @file
* @brief Segment Acknowledgment (SegmentAck) PDU encode and decode functions
* @author Julien Bennet <antibarbie@users.sourceforge.net>
* @date 2010
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
*/
#include "bacnet/segmentack.h"
/**
* @brief Method to encode the segment ack .
* @param apdu[in] Pointer to the buffer for encoding.
* @param negativeack[in] Acknowledgment for the segment.
* @param server[in] Set to True if the acknowledgment is from the server, else
* false.
* @param invoke_id[in] Invoke Id
* @param sequence_number[in] Sequence number of the segment to be acknowledged
* @param actual_window_size[in] Actual window size.
* @return Length of encoded data or zero on error.
*/
int segmentack_encode_apdu(
uint8_t *apdu,
bool negativeack,
bool server,
uint8_t invoke_id,
uint8_t sequence_number,
uint8_t actual_window_size)
{
int apdu_len = 0; /* total length of the apdu, return value */
uint8_t server_code = server ? 0x01 : 0x00;
uint8_t nak_code = negativeack ? 0x02 : 0x00;
if (apdu) {
apdu[0] = PDU_TYPE_SEGMENT_ACK | server_code | nak_code;
apdu[1] = invoke_id;
apdu[2] = sequence_number;
apdu[3] = actual_window_size;
apdu_len = 4;
}
return apdu_len;
}
/**
* @brief Method to decode the segment ack service request
* @param apdu[in] The apdu portion of the ACK reply.
* @param apdu_len[in] The total length of the apdu.
* @param invoke_id[out] Invoke Id of the request.
* @param sequence_number[out] Sequence number of the segment received.
* @param actual_window_size[out] Actual window size.
* @return Length of decoded data or zero on error.
*/
int segmentack_decode_service_request(
uint8_t *apdu,
unsigned apdu_len,
uint8_t *invoke_id,
uint8_t *sequence_number,
uint8_t *actual_window_size)
{
int len = 0;
int apdu_header_size = 3;
if (apdu_len >= apdu_header_size) {
if (invoke_id) {
*invoke_id = apdu[0];
}
if (sequence_number) {
*sequence_number = apdu[1];
}
if (actual_window_size) {
*actual_window_size = apdu[2];
}
/* three bytes successfully decoded: invoke_id, sequence_number,
* and actual_window_size */
len = apdu_header_size;
}
return len;
}
+41
View File
@@ -0,0 +1,41 @@
/**
* @file
* @brief Segment Acknowledgment (SegmentAck) PDU encode and decode functions
* @author Julien Bennet <antibarbie@users.sourceforge.net>
* @date 2010
* @copyright SPDX-License-Identifier: MIT
*/
#ifndef BACNET_SEGMENT_ACK_H
#define BACNET_SEGMENT_ACK_H
#include <stdint.h>
#include <stdbool.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
int segmentack_encode_apdu(
uint8_t *apdu,
bool negativeack,
bool server,
uint8_t invoke_id,
uint8_t sequence_number,
uint8_t actual_window_size);
BACNET_STACK_EXPORT
int segmentack_decode_service_request(
uint8_t *apdu,
unsigned apdu_len,
uint8_t *invoke_id,
uint8_t *sequence_number,
uint8_t *actual_window_size);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* BACNET_SEGMENT_ACK_H */