Added segmentation support for server reply. (#974)
Added segmentation support for server devices for some services. Configure BACNET_SEGMENTATION_ENABLED=1 to include in the library, and adjust BACNET_MAX_SEGMENTS_ACCEPTED for maximum number of segments.
This commit is contained in:
@@ -16,6 +16,11 @@ The git repositories are hosted at the following sites:
|
|||||||
|
|
||||||
### Security
|
### Security
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Added segmentation support for server devices for some services.
|
||||||
|
Configure BACNET_SEGMENTATION_ENABLED=1 to include in the library,
|
||||||
|
and adjust BACNET_MAX_SEGMENTS_ACCEPTED for maximum number of segments. (#974)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -672,6 +677,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/timer_value.c
|
src/bacnet/timer_value.c
|
||||||
src/bacnet/timer_value.h
|
src/bacnet/timer_value.h
|
||||||
src/bacnet/timestamp.c
|
src/bacnet/timestamp.c
|
||||||
@@ -723,6 +730,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>
|
||||||
$<$<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)
|
||||||
@@ -1275,3 +1283,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}\"")
|
||||||
|
|||||||
@@ -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 $@
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -174,6 +174,20 @@ static void Init_Service_Handlers(void)
|
|||||||
(unsigned)object_data.object_instance);
|
(unsigned)object_data.object_instance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
printf("Segmentation is enabled.\n");
|
||||||
|
/* create extra object to stress the object list */
|
||||||
|
for (i = 0; i < 500; i++) {
|
||||||
|
object_data.object_instance = BACNET_MAX_INSTANCE;
|
||||||
|
object_data.object_type = OBJECT_ANALOG_INPUT;
|
||||||
|
if (Device_Create_Object(&object_data)) {
|
||||||
|
printf(
|
||||||
|
"Created object %s-%u\n",
|
||||||
|
bactext_object_type_name(object_data.object_type),
|
||||||
|
(unsigned)object_data.object_instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
/* we need to handle who-is to support dynamic device binding */
|
/* we need to handle who-is to support dynamic device binding */
|
||||||
apdu_set_unconfirmed_handler(
|
apdu_set_unconfirmed_handler(
|
||||||
SERVICE_UNCONFIRMED_WHO_IS, handler_who_is_who_am_i_unicast);
|
SERVICE_UNCONFIRMED_WHO_IS, handler_who_is_who_am_i_unicast);
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
@@ -32,6 +32,21 @@ typedef struct _confirmed_service_ack_data {
|
|||||||
uint8_t proposed_window_number;
|
uint8_t proposed_window_number;
|
||||||
} BACNET_CONFIRMED_SERVICE_ACK_DATA;
|
} BACNET_CONFIRMED_SERVICE_ACK_DATA;
|
||||||
|
|
||||||
|
typedef struct BACnet_Apdu_Fixed_Header {
|
||||||
|
/* pdu type Confirmed Request or Complex ACK */
|
||||||
|
uint8_t pdu_type;
|
||||||
|
union {
|
||||||
|
/* Data for pdu type PDU_TYPE_CONFIRMED_SERVICE_REQUEST */
|
||||||
|
struct _confirmed_service_data request_data;
|
||||||
|
/* Data for pdu type PDU_TYPE_COMPLEX_ACK */
|
||||||
|
struct _confirmed_service_ack_data ack_data;
|
||||||
|
/* Common data for both types */
|
||||||
|
struct _confirmed_service_ack_data common_data;
|
||||||
|
} service_data;
|
||||||
|
/* Service number */
|
||||||
|
uint8_t service_choice;
|
||||||
|
} BACNET_APDU_FIXED_HEADER;
|
||||||
|
|
||||||
uint8_t apdu_network_priority(void);
|
uint8_t apdu_network_priority(void);
|
||||||
void apdu_network_priority_set(uint8_t pri);
|
void apdu_network_priority_set(uint8_t pri);
|
||||||
|
|
||||||
|
|||||||
@@ -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) | \
|
||||||
|
|||||||
@@ -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,19 @@ bool address_get_by_device(
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Return the cached addresBACNET_STACK_EXPORT
|
||||||
|
s 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.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -1143,7 +1143,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
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1614,8 +1618,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;
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ static uint16_t Timeout_Milliseconds = 3000;
|
|||||||
/* Number of APDU Retries */
|
/* Number of APDU Retries */
|
||||||
static uint8_t Number_Of_Retries = 3;
|
static uint8_t Number_Of_Retries = 3;
|
||||||
static uint8_t Local_Network_Priority; /* Fixing test 10.1.2 Network priority */
|
static uint8_t Local_Network_Priority; /* Fixing test 10.1.2 Network priority */
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/* APDU Segment Timeout in Milliseconds */
|
||||||
|
static uint16_t Segment_Timeout_Milliseconds = 5000;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* a simple table for crossing the services supported */
|
/* a simple table for crossing the services supported */
|
||||||
static BACNET_SERVICES_SUPPORTED
|
static BACNET_SERVICES_SUPPORTED
|
||||||
@@ -552,6 +556,109 @@ static bool apdu_unconfirmed_dcc_disabled(uint8_t service_choice)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Invoke special handler for confirmed service */
|
||||||
|
static void invoke_confirmed_service_service_request(
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data,
|
||||||
|
uint8_t service_choice,
|
||||||
|
uint8_t *service_request,
|
||||||
|
uint32_t service_request_len)
|
||||||
|
{
|
||||||
|
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
||||||
|
/* When network communications are completely disabled,
|
||||||
|
only DeviceCommunicationControl and ReinitializeDevice
|
||||||
|
APDUs shall be processed and no messages shall be
|
||||||
|
initiated. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
||||||
|
(Confirmed_Function[service_choice])) {
|
||||||
|
Confirmed_Function[service_choice](
|
||||||
|
service_request, service_request_len, src, service_data);
|
||||||
|
} else if (Unrecognized_Service_Handler) {
|
||||||
|
Unrecognized_Service_Handler(
|
||||||
|
service_request, service_request_len, src, service_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/** Handler for messages with segmentation :
|
||||||
|
- store new packet if sequence number is ok
|
||||||
|
- NACK packet if sequence number is not ok
|
||||||
|
- call the final functions with reassembled data when last packet ok is
|
||||||
|
received
|
||||||
|
*/
|
||||||
|
static void apdu_handler_confirmed_service_segment(
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
uint8_t *apdu, /* APDU data */
|
||||||
|
uint32_t apdu_len)
|
||||||
|
{
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
uint8_t internal_service_id = 0;
|
||||||
|
uint8_t service_choice = 0;
|
||||||
|
uint8_t *service_request = NULL;
|
||||||
|
uint16_t service_request_len = 0;
|
||||||
|
uint32_t len = 0; /* counts where we are in PDU */
|
||||||
|
bool segment_ok;
|
||||||
|
|
||||||
|
len = apdu_decode_confirmed_service_request(
|
||||||
|
&apdu[0], /* APDU data */
|
||||||
|
apdu_len, &service_data, &service_choice, &service_request,
|
||||||
|
&service_request_len);
|
||||||
|
if (len == 0) {
|
||||||
|
/* service data unable to be decoded - simply drop */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* new segment : memorize it */
|
||||||
|
segment_ok = tsm_set_segmented_confirmed_service_received(
|
||||||
|
src, &service_data, &internal_service_id, &service_request,
|
||||||
|
&service_request_len);
|
||||||
|
/* last segment */
|
||||||
|
if (segment_ok && !service_data.more_follows) {
|
||||||
|
/* Clear peer information */
|
||||||
|
tsm_clear_peer_id(internal_service_id);
|
||||||
|
/* Invoke service handler */
|
||||||
|
invoke_confirmed_service_service_request(
|
||||||
|
src, &service_data, service_choice, service_request,
|
||||||
|
service_request_len);
|
||||||
|
/* We must free invoke_id, and associated data */
|
||||||
|
tsm_free_invoke_id_check(internal_service_id, NULL, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Handler for normal message without segmentation, or segmented complete
|
||||||
|
* message reassembled all-in-one */
|
||||||
|
static void apdu_handler_confirmed_service(
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
uint8_t *apdu, /* APDU data */
|
||||||
|
uint32_t apdu_len)
|
||||||
|
{
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
||||||
|
uint8_t service_choice = 0;
|
||||||
|
uint8_t *service_request = NULL;
|
||||||
|
uint16_t service_request_len = 0;
|
||||||
|
uint32_t len = 0; /* counts where we are in PDU */
|
||||||
|
|
||||||
|
len = apdu_decode_confirmed_service_request(
|
||||||
|
&apdu[0], /* APDU data */
|
||||||
|
apdu_len, &service_data, &service_choice, &service_request,
|
||||||
|
&service_request_len);
|
||||||
|
if (len == 0) {
|
||||||
|
/* service data unable to be decoded - simply drop */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/* Check for unexpected request is received in active TSM state */
|
||||||
|
if (tsm_is_invalid_apdu_in_this_state(src, &service_data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
invoke_confirmed_service_service_request(
|
||||||
|
src, &service_data, service_choice, service_request,
|
||||||
|
service_request_len);
|
||||||
|
}
|
||||||
|
|
||||||
/** Process the APDU header and invoke the appropriate service handler
|
/** Process the APDU header and invoke the appropriate service handler
|
||||||
* to manage the received request.
|
* to manage the received request.
|
||||||
* Almost all requests and ACKs invoke this function.
|
* Almost all requests and ACKs invoke this function.
|
||||||
@@ -567,11 +674,15 @@ void apdu_handler(
|
|||||||
uint16_t apdu_len)
|
uint16_t apdu_len)
|
||||||
{
|
{
|
||||||
BACNET_PDU_TYPE pdu_type;
|
BACNET_PDU_TYPE pdu_type;
|
||||||
BACNET_CONFIRMED_SERVICE_DATA service_data = { 0 };
|
|
||||||
uint8_t service_choice = 0;
|
uint8_t service_choice = 0;
|
||||||
uint8_t *service_request = NULL;
|
uint8_t *service_request = NULL;
|
||||||
uint16_t service_request_len = 0;
|
uint16_t service_request_len = 0;
|
||||||
int len = 0; /* counts where we are in PDU */
|
int len = 0; /* counts where we are in PDU */
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
uint8_t sequence_number = 0;
|
||||||
|
uint8_t actual_window_size = 0;
|
||||||
|
bool nak = false;
|
||||||
|
#endif
|
||||||
#if !BACNET_SVC_SERVER
|
#if !BACNET_SVC_SERVER
|
||||||
uint8_t invoke_id = 0;
|
uint8_t invoke_id = 0;
|
||||||
BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 };
|
BACNET_CONFIRMED_SERVICE_ACK_DATA service_ack_data = { 0 };
|
||||||
@@ -590,27 +701,14 @@ void apdu_handler(
|
|||||||
pdu_type = apdu[0] & 0xF0;
|
pdu_type = apdu[0] & 0xF0;
|
||||||
switch (pdu_type) {
|
switch (pdu_type) {
|
||||||
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||||
len = apdu_decode_confirmed_service_request(
|
/* segmented_message_reception ? */
|
||||||
apdu, apdu_len, &service_data, &service_choice,
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
&service_request, &service_request_len);
|
if (apdu[0] & BIT(3)) {
|
||||||
if (len == 0) {
|
apdu_handler_confirmed_service_segment(src, apdu, apdu_len);
|
||||||
/* service data unable to be decoded - simply drop */
|
} else
|
||||||
break;
|
#endif
|
||||||
}
|
{
|
||||||
if (apdu_confirmed_dcc_disabled(service_choice)) {
|
apdu_handler_confirmed_service(src, apdu, apdu_len);
|
||||||
/* When network communications are completely disabled,
|
|
||||||
only DeviceCommunicationControl and ReinitializeDevice
|
|
||||||
APDUs shall be processed and no messages shall be
|
|
||||||
initiated. */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
|
|
||||||
(Confirmed_Function[service_choice])) {
|
|
||||||
Confirmed_Function[service_choice](
|
|
||||||
service_request, service_request_len, src, &service_data);
|
|
||||||
} else if (Unrecognized_Service_Handler) {
|
|
||||||
Unrecognized_Service_Handler(
|
|
||||||
service_request, service_request_len, src, &service_data);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST:
|
||||||
@@ -685,9 +783,24 @@ void apdu_handler(
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_SEGMENT_ACK:
|
case PDU_TYPE_SEGMENT_ACK:
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
/* FIXME: what about a denial of service attack here?
|
/* FIXME: what about a denial of service attack here?
|
||||||
we could check src to see if that matched the tsm */
|
we could check src to see if that matched the tsm */
|
||||||
tsm_free_invoke_id(invoke_id);
|
tsm_free_invoke_id(invoke_id);
|
||||||
|
#else
|
||||||
|
if (apdu_len < 4) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
server = apdu[0] & 0x01;
|
||||||
|
nak = apdu[0] & 0x02;
|
||||||
|
invoke_id = apdu[1];
|
||||||
|
sequence_number = apdu[2];
|
||||||
|
actual_window_size = apdu[3];
|
||||||
|
/* we care because we support segmented message sending */
|
||||||
|
tsm_segmentack_received(
|
||||||
|
invoke_id, sequence_number, actual_window_size, nak, server,
|
||||||
|
src);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_ERROR:
|
case PDU_TYPE_ERROR:
|
||||||
if (apdu_len < 3) {
|
if (apdu_len < 3) {
|
||||||
@@ -714,7 +827,12 @@ void apdu_handler(
|
|||||||
(BACNET_ERROR_CODE)error_code);
|
(BACNET_ERROR_CODE)error_code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/*Release the data*/
|
||||||
|
tsm_free_invoke_id_segmentation(src, invoke_id);
|
||||||
|
#else
|
||||||
tsm_free_invoke_id(invoke_id);
|
tsm_free_invoke_id(invoke_id);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_REJECT:
|
case PDU_TYPE_REJECT:
|
||||||
if (apdu_len < 3) {
|
if (apdu_len < 3) {
|
||||||
@@ -725,7 +843,12 @@ void apdu_handler(
|
|||||||
if (Reject_Function) {
|
if (Reject_Function) {
|
||||||
Reject_Function(src, invoke_id, reason);
|
Reject_Function(src, invoke_id, reason);
|
||||||
}
|
}
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/*Release the data*/
|
||||||
|
tsm_free_invoke_id_segmentation(src, invoke_id);
|
||||||
|
#else
|
||||||
tsm_free_invoke_id(invoke_id);
|
tsm_free_invoke_id(invoke_id);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case PDU_TYPE_ABORT:
|
case PDU_TYPE_ABORT:
|
||||||
if (apdu_len < 3) {
|
if (apdu_len < 3) {
|
||||||
@@ -734,13 +857,156 @@ void apdu_handler(
|
|||||||
server = apdu[0] & 0x01;
|
server = apdu[0] & 0x01;
|
||||||
invoke_id = apdu[1];
|
invoke_id = apdu[1];
|
||||||
reason = apdu[2];
|
reason = apdu[2];
|
||||||
if (Abort_Function) {
|
if (!server) {
|
||||||
Abort_Function(src, invoke_id, reason, server);
|
/*AbortPDU_Received*/
|
||||||
|
if (Abort_Function) {
|
||||||
|
Abort_Function(src, invoke_id, reason, server);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
else {
|
||||||
|
/*SendAbort*/
|
||||||
|
tsm_abort_pdu_send(invoke_id, src, reason, server);
|
||||||
|
}
|
||||||
|
/*Release the data*/
|
||||||
|
tsm_free_invoke_id_segmentation(src, invoke_id);
|
||||||
|
#else
|
||||||
tsm_free_invoke_id(invoke_id);
|
tsm_free_invoke_id(invoke_id);
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/*Return the APDU segment timeout*/
|
||||||
|
uint16_t apdu_segment_timeout(void)
|
||||||
|
{
|
||||||
|
return Segment_Timeout_Milliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Set the APDU segment timeout*/
|
||||||
|
void apdu_segment_timeout_set(uint16_t milliseconds)
|
||||||
|
{
|
||||||
|
Segment_Timeout_Milliseconds = milliseconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Process the APDU header and invoke the appropriate service handler
|
||||||
|
* to manage the received request.
|
||||||
|
* Almost all requests and ACKs invoke this function.
|
||||||
|
* @ingroup MISCHNDLR
|
||||||
|
*
|
||||||
|
* @param apdu [in] The apdu portion of the response, to be sent
|
||||||
|
* @param fixed_pdu_header [in] The apdu header for the response
|
||||||
|
* @return apdu_length[out] The length of the apdu header
|
||||||
|
*/
|
||||||
|
int apdu_encode_fixed_header(
|
||||||
|
uint8_t *apdu, BACNET_APDU_FIXED_HEADER *fixed_pdu_header)
|
||||||
|
{
|
||||||
|
int apdu_len = 0;
|
||||||
|
switch (fixed_pdu_header->pdu_type) {
|
||||||
|
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->pdu_type
|
||||||
|
/* flag 'SA' if we accept many segments */
|
||||||
|
| (fixed_pdu_header->service_data.request_data
|
||||||
|
.segmented_response_accepted
|
||||||
|
? 0x02
|
||||||
|
: 0x00)
|
||||||
|
/* flag 'MOR' if we more segments are coming */
|
||||||
|
| (fixed_pdu_header->service_data.request_data.more_follows
|
||||||
|
? 0x04
|
||||||
|
: 0x00)
|
||||||
|
/* flag 'SEG' if we more segments are coming */
|
||||||
|
| (fixed_pdu_header->service_data.request_data.segmented_message
|
||||||
|
? 0x08
|
||||||
|
: 0x00);
|
||||||
|
apdu[apdu_len++] = encode_max_segs_max_apdu(
|
||||||
|
fixed_pdu_header->service_data.request_data.max_segs,
|
||||||
|
fixed_pdu_header->service_data.request_data.max_resp);
|
||||||
|
apdu[apdu_len++] =
|
||||||
|
fixed_pdu_header->service_data.request_data.invoke_id;
|
||||||
|
/* extra data for segmented messages sending */
|
||||||
|
if (fixed_pdu_header->service_data.request_data.segmented_message) {
|
||||||
|
/* packet sequence number */
|
||||||
|
apdu[apdu_len++] =
|
||||||
|
fixed_pdu_header->service_data.request_data.sequence_number;
|
||||||
|
/* window size proposal */
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->service_data.request_data
|
||||||
|
.proposed_window_number;
|
||||||
|
}
|
||||||
|
/* service choice */
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->service_choice;
|
||||||
|
break;
|
||||||
|
case PDU_TYPE_COMPLEX_ACK:
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->pdu_type
|
||||||
|
/* flag 'MOR' if we more segments are coming */
|
||||||
|
| (fixed_pdu_header->service_data.ack_data.more_follows ? 0x04
|
||||||
|
: 0x00)
|
||||||
|
/* flag 'SEG' if we more segments are coming */
|
||||||
|
| (fixed_pdu_header->service_data.ack_data.segmented_message
|
||||||
|
? 0x08
|
||||||
|
: 0x00);
|
||||||
|
apdu[apdu_len++] =
|
||||||
|
fixed_pdu_header->service_data.ack_data.invoke_id;
|
||||||
|
/* extra data for segmented messages sending */
|
||||||
|
if (fixed_pdu_header->service_data.ack_data.segmented_message) {
|
||||||
|
/* packet sequence number */
|
||||||
|
apdu[apdu_len++] =
|
||||||
|
fixed_pdu_header->service_data.ack_data.sequence_number;
|
||||||
|
/* window size proposal */
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->service_data.ack_data
|
||||||
|
.proposed_window_number;
|
||||||
|
}
|
||||||
|
/* service choice */
|
||||||
|
apdu[apdu_len++] = fixed_pdu_header->service_choice;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return apdu_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Handler to assign the header fields to the response
|
||||||
|
* The response can be segmented/unsegmented
|
||||||
|
*
|
||||||
|
* @param fixed_pdu_header [in] The apdu header of the response, to be sent.
|
||||||
|
* @param pdu_type [in] The pdu_type of the response.
|
||||||
|
* @param invoke_id [in] The invoike_id of the response
|
||||||
|
* @param service[in] The service choice for which the response has to be
|
||||||
|
* processed
|
||||||
|
* @param max_apdu[in] The maximum apdu length
|
||||||
|
*/
|
||||||
|
void apdu_init_fixed_header(
|
||||||
|
BACNET_APDU_FIXED_HEADER *fixed_pdu_header,
|
||||||
|
uint8_t pdu_type,
|
||||||
|
uint8_t invoke_id,
|
||||||
|
uint8_t service,
|
||||||
|
int max_apdu)
|
||||||
|
{
|
||||||
|
fixed_pdu_header->pdu_type = pdu_type;
|
||||||
|
|
||||||
|
fixed_pdu_header->service_data.common_data.invoke_id = invoke_id;
|
||||||
|
fixed_pdu_header->service_data.common_data.more_follows = false;
|
||||||
|
fixed_pdu_header->service_data.common_data.proposed_window_number = 0;
|
||||||
|
fixed_pdu_header->service_data.common_data.sequence_number = 0;
|
||||||
|
fixed_pdu_header->service_data.common_data.segmented_message = false;
|
||||||
|
switch (pdu_type) {
|
||||||
|
case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
|
||||||
|
fixed_pdu_header->service_data.request_data.max_segs =
|
||||||
|
BACNET_MAX_SEGMENTS_ACCEPTED;
|
||||||
|
/* allow to specify a lower APDU size : support arbitrary reduction
|
||||||
|
* of APDU packets between peers */
|
||||||
|
fixed_pdu_header->service_data.request_data.max_resp =
|
||||||
|
max_apdu < MAX_APDU ? max_apdu : MAX_APDU;
|
||||||
|
fixed_pdu_header->service_data.request_data
|
||||||
|
.segmented_response_accepted = BACNET_MAX_SEGMENTS_ACCEPTED > 1;
|
||||||
|
break;
|
||||||
|
case PDU_TYPE_COMPLEX_ACK:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fixed_pdu_header->service_choice = service;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -162,6 +162,24 @@ void apdu_handler(
|
|||||||
uint8_t *apdu, /* APDU data */
|
uint8_t *apdu, /* APDU data */
|
||||||
uint16_t pdu_len); /* for confirmed messages */
|
uint16_t pdu_len); /* for confirmed messages */
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint16_t apdu_segment_timeout(void);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void apdu_segment_timeout_set(uint16_t milliseconds);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int apdu_encode_fixed_header(
|
||||||
|
uint8_t *apdu, BACNET_APDU_FIXED_HEADER *fixed_pdu_header);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void apdu_init_fixed_header(
|
||||||
|
BACNET_APDU_FIXED_HEADER *fixed_pdu_header,
|
||||||
|
uint8_t pdu_type,
|
||||||
|
uint8_t invoke_id,
|
||||||
|
uint8_t service,
|
||||||
|
int max_apdu);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ void handler_device_communication_control(
|
|||||||
debug_print("DeviceCommunicationControl: "
|
debug_print("DeviceCommunicationControl: "
|
||||||
"Missing Required Parameter. Sending Reject!\n");
|
"Missing Required Parameter. Sending Reject!\n");
|
||||||
goto DCC_FAILURE;
|
goto DCC_FAILURE;
|
||||||
} else if (service_data->segmented_message) {
|
}
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
|
else if (service_data->segmented_message) {
|
||||||
len = abort_encode_apdu(
|
len = abort_encode_apdu(
|
||||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||||
@@ -116,6 +118,7 @@ void handler_device_communication_control(
|
|||||||
"Sending Abort - segmented message.\n");
|
"Sending Abort - segmented message.\n");
|
||||||
goto DCC_FAILURE;
|
goto DCC_FAILURE;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* decode the service request only */
|
/* decode the service request only */
|
||||||
len = dcc_decode_service_request(
|
len = dcc_decode_service_request(
|
||||||
service_request, service_len, &timeDuration, &state, &password);
|
service_request, service_len, &timeDuration, &state, &password);
|
||||||
|
|||||||
@@ -71,13 +71,16 @@ void handler_reinitialize_device(
|
|||||||
debug_print("ReinitializeDevice: Missing Required Parameter. "
|
debug_print("ReinitializeDevice: Missing Required Parameter. "
|
||||||
"Sending Reject!\n");
|
"Sending Reject!\n");
|
||||||
goto RD_ABORT;
|
goto RD_ABORT;
|
||||||
} else if (service_data->segmented_message) {
|
}
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
|
else if (service_data->segmented_message) {
|
||||||
len = abort_encode_apdu(
|
len = abort_encode_apdu(
|
||||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||||
debug_print("ReinitializeDevice: Sending Abort - segmented message.\n");
|
debug_print("ReinitializeDevice: Sending Abort - segmented message.\n");
|
||||||
goto RD_ABORT;
|
goto RD_ABORT;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
/* decode the service request only */
|
/* decode the service request only */
|
||||||
len = rd_decode_service_request(
|
len = rd_decode_service_request(
|
||||||
service_request, service_len, &rd_data.state, &rd_data.password);
|
service_request, service_len, &rd_data.state, &rd_data.password);
|
||||||
|
|||||||
@@ -30,6 +30,12 @@
|
|||||||
#include "bacnet/basic/sys/debug.h"
|
#include "bacnet/basic/sys/debug.h"
|
||||||
#include "bacnet/datalink/datalink.h"
|
#include "bacnet/datalink/datalink.h"
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
#define BACNET_RP_BUFFER_OVERFLOW ERROR_CODE_ABORT_BUFFER_OVERFLOW
|
||||||
|
#else
|
||||||
|
#define BACNET_RP_BUFFER_OVERFLOW ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
/** @file h_rp.c Handles Read Property requests. */
|
/** @file h_rp.c Handles Read Property requests. */
|
||||||
|
|
||||||
/** Handler for a ReadProperty Service request.
|
/** Handler for a ReadProperty Service request.
|
||||||
@@ -38,7 +44,7 @@
|
|||||||
* by a call to apdu_set_confirmed_handler().
|
* by a call to apdu_set_confirmed_handler().
|
||||||
* This handler builds a response packet, which is
|
* This handler builds a response packet, which is
|
||||||
* - an Abort if
|
* - an Abort if
|
||||||
* - the message is segmented
|
* - the message is segmented and segmentation is not supported
|
||||||
* - if decoding fails
|
* - if decoding fails
|
||||||
* - if the response would be too large
|
* - if the response would be too large
|
||||||
* - the result from Device_Read_Property(), if it succeeds
|
* - the result from Device_Read_Property(), if it succeeds
|
||||||
@@ -59,6 +65,7 @@ void handler_read_property(
|
|||||||
{
|
{
|
||||||
BACNET_READ_PROPERTY_DATA rpdata;
|
BACNET_READ_PROPERTY_DATA rpdata;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
|
int max_resp = 0;
|
||||||
int pdu_len = 0;
|
int pdu_len = 0;
|
||||||
int apdu_len = -1;
|
int apdu_len = -1;
|
||||||
int npdu_len = -1;
|
int npdu_len = -1;
|
||||||
@@ -66,9 +73,13 @@ void handler_read_property(
|
|||||||
bool error = true; /* assume that there is an error */
|
bool error = true; /* assume that there is an error */
|
||||||
int bytes_sent = 0;
|
int bytes_sent = 0;
|
||||||
BACNET_ADDRESS my_address;
|
BACNET_ADDRESS my_address;
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
BACNET_APDU_FIXED_HEADER apdu_fixed_header;
|
||||||
|
int apdu_header_len = 3;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* configure default error code as an abort since it is common */
|
/* configure default error code as an abort since it is common */
|
||||||
rpdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
rpdata.error_code = BACNET_RP_BUFFER_OVERFLOW;
|
||||||
/* encode the NPDU portion of the packet */
|
/* encode the NPDU portion of the packet */
|
||||||
datalink_get_my_address(&my_address);
|
datalink_get_my_address(&my_address);
|
||||||
npdu_encode_npdu_data(&npdu_data, false, service_data->priority);
|
npdu_encode_npdu_data(&npdu_data, false, service_data->priority);
|
||||||
@@ -82,10 +93,12 @@ void handler_read_property(
|
|||||||
len = BACNET_STATUS_REJECT;
|
len = BACNET_STATUS_REJECT;
|
||||||
rpdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
rpdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
||||||
debug_print("RP: Missing Required Parameter. Sending Reject!\n");
|
debug_print("RP: Missing Required Parameter. Sending Reject!\n");
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
} else if (service_data->segmented_message) {
|
} else if (service_data->segmented_message) {
|
||||||
/* we don't support segmentation - send an abort */
|
/* we don't support segmentation - send an abort */
|
||||||
len = BACNET_STATUS_ABORT;
|
len = BACNET_STATUS_ABORT;
|
||||||
debug_print("RP: Segmented message. Sending Abort!\n");
|
debug_print("RP: Segmented message. Sending Abort!\n");
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
len = rp_decode_service_request(service_request, service_len, &rpdata);
|
len = rp_decode_service_request(service_request, service_len, &rpdata);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
@@ -116,6 +129,13 @@ void handler_read_property(
|
|||||||
rpdata.object_instance = Network_Port_Index_To_Instance(0);
|
rpdata.object_instance = Network_Port_Index_To_Instance(0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
apdu_init_fixed_header(
|
||||||
|
&apdu_fixed_header, PDU_TYPE_COMPLEX_ACK,
|
||||||
|
service_data->invoke_id, SERVICE_CONFIRMED_READ_PROPERTY,
|
||||||
|
service_data->max_resp);
|
||||||
|
#endif
|
||||||
apdu_len = rp_ack_encode_apdu_init(
|
apdu_len = rp_ack_encode_apdu_init(
|
||||||
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id,
|
&Handler_Transmit_Buffer[npdu_len], service_data->invoke_id,
|
||||||
&rpdata);
|
&rpdata);
|
||||||
@@ -134,7 +154,25 @@ void handler_read_property(
|
|||||||
len = rp_ack_encode_apdu_object_property_end(
|
len = rp_ack_encode_apdu_object_property_end(
|
||||||
&Handler_Transmit_Buffer[npdu_len + apdu_len]);
|
&Handler_Transmit_Buffer[npdu_len + apdu_len]);
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
if (apdu_len > service_data->max_resp) {
|
/* pick the smaller response packet: ours or theirs */
|
||||||
|
max_resp = min(service_data->max_resp, MAX_APDU);
|
||||||
|
if (apdu_len > max_resp) {
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
if (service_data->segmented_response_accepted) {
|
||||||
|
npdu_encode_npdu_data(
|
||||||
|
&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||||
|
npdu_len = npdu_encode_pdu(
|
||||||
|
&Handler_Transmit_Buffer[0], src, &my_address,
|
||||||
|
&npdu_data);
|
||||||
|
|
||||||
|
tsm_set_complexack_transaction(
|
||||||
|
src, &npdu_data, &apdu_fixed_header, service_data,
|
||||||
|
&Handler_Transmit_Buffer
|
||||||
|
[npdu_len + apdu_header_len],
|
||||||
|
(apdu_len - apdu_header_len));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
/* too big for the sender - send an abort!
|
/* too big for the sender - send an abort!
|
||||||
Setting of error code needed here as read property
|
Setting of error code needed here as read property
|
||||||
processing may have overridden the default set at start
|
processing may have overridden the default set at start
|
||||||
@@ -143,6 +181,7 @@ void handler_read_property(
|
|||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
len = BACNET_STATUS_ABORT;
|
len = BACNET_STATUS_ABORT;
|
||||||
debug_print("RP: Message too large.\n");
|
debug_print("RP: Message too large.\n");
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
debug_print("RP: Sending Ack!\n");
|
debug_print("RP: Sending Ack!\n");
|
||||||
error = false;
|
error = false;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
/* BACnet Stack defines - first */
|
/* BACnet Stack defines - first */
|
||||||
#include "bacnet/bacdef.h"
|
#include "bacnet/bacdef.h"
|
||||||
/* BACnet Stack API */
|
/* BACnet Stack API */
|
||||||
@@ -33,7 +34,17 @@
|
|||||||
#include "bacnet/basic/sys/debug.h"
|
#include "bacnet/basic/sys/debug.h"
|
||||||
#include "bacnet/datalink/datalink.h"
|
#include "bacnet/datalink/datalink.h"
|
||||||
|
|
||||||
static uint8_t Temp_Buf[MAX_APDU] = { 0 };
|
/* Smaller single threaded implementations prefer a
|
||||||
|
single buffer for encoding each property from the RPM request. */
|
||||||
|
#ifndef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK
|
||||||
|
static uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
#define BACNET_RPM_BUFFER_OVERFLOW ERROR_CODE_ABORT_BUFFER_OVERFLOW
|
||||||
|
#else
|
||||||
|
#define BACNET_RPM_BUFFER_OVERFLOW ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fetches the lists of properties (array of BACNET_PROPERTY_ID's) for
|
* @brief Fetches the lists of properties (array of BACNET_PROPERTY_ID's) for
|
||||||
@@ -124,12 +135,15 @@ static int RPM_Encode_Property(
|
|||||||
size_t copy_len = 0;
|
size_t copy_len = 0;
|
||||||
int apdu_len = 0;
|
int apdu_len = 0;
|
||||||
BACNET_READ_PROPERTY_DATA rpdata;
|
BACNET_READ_PROPERTY_DATA rpdata;
|
||||||
|
#ifdef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK
|
||||||
|
uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 };
|
||||||
|
#endif
|
||||||
|
|
||||||
len = rpm_ack_encode_apdu_object_property(
|
len = rpm_ack_encode_apdu_object_property(
|
||||||
&Temp_Buf[0], rpmdata->object_property, rpmdata->array_index);
|
&RPM_Prop_Buffer[0], rpmdata->object_property, rpmdata->array_index);
|
||||||
copy_len = memcopy(&apdu[0], &Temp_Buf[0], offset, len, max_apdu);
|
copy_len = memcopy(&apdu[0], &RPM_Prop_Buffer[0], offset, len, max_apdu);
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
return BACNET_STATUS_ABORT;
|
return BACNET_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
@@ -139,8 +153,8 @@ static int RPM_Encode_Property(
|
|||||||
rpdata.object_instance = rpmdata->object_instance;
|
rpdata.object_instance = rpmdata->object_instance;
|
||||||
rpdata.object_property = rpmdata->object_property;
|
rpdata.object_property = rpmdata->object_property;
|
||||||
rpdata.array_index = rpmdata->array_index;
|
rpdata.array_index = rpmdata->array_index;
|
||||||
rpdata.application_data = &Temp_Buf[0];
|
rpdata.application_data = &RPM_Prop_Buffer[0];
|
||||||
rpdata.application_data_len = sizeof(Temp_Buf);
|
rpdata.application_data_len = sizeof(RPM_Prop_Buffer);
|
||||||
|
|
||||||
if ((rpmdata->object_property == PROP_ALL) ||
|
if ((rpmdata->object_property == PROP_ALL) ||
|
||||||
(rpmdata->object_property == PROP_REQUIRED) ||
|
(rpmdata->object_property == PROP_REQUIRED) ||
|
||||||
@@ -161,21 +175,21 @@ static int RPM_Encode_Property(
|
|||||||
}
|
}
|
||||||
/* error was returned - encode that for the response */
|
/* error was returned - encode that for the response */
|
||||||
len = rpm_ack_encode_apdu_object_property_error(
|
len = rpm_ack_encode_apdu_object_property_error(
|
||||||
&Temp_Buf[0], rpdata.error_class, rpdata.error_code);
|
&RPM_Prop_Buffer[0], rpdata.error_class, rpdata.error_code);
|
||||||
copy_len =
|
copy_len = memcopy(
|
||||||
memcopy(&apdu[0], &Temp_Buf[0], offset + apdu_len, len, max_apdu);
|
&apdu[0], &RPM_Prop_Buffer[0], offset + apdu_len, len, max_apdu);
|
||||||
|
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
return BACNET_STATUS_ABORT;
|
return BACNET_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
} else if ((offset + apdu_len + 1 + len + 1) < max_apdu) {
|
} else if ((offset + apdu_len + 1 + len + 1) < max_apdu) {
|
||||||
/* enough room to fit the property value and tags */
|
/* enough room to fit the property value and tags */
|
||||||
len = rpm_ack_encode_apdu_object_property_value(
|
len = rpm_ack_encode_apdu_object_property_value(
|
||||||
&apdu[offset + apdu_len], &Temp_Buf[0], len);
|
&apdu[offset + apdu_len], &RPM_Prop_Buffer[0], len);
|
||||||
} else {
|
} else {
|
||||||
/* not enough room - abort! */
|
/* not enough room - abort! */
|
||||||
rpmdata->error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
rpmdata->error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
return BACNET_STATUS_ABORT;
|
return BACNET_STATUS_ABORT;
|
||||||
}
|
}
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
@@ -189,7 +203,7 @@ static int RPM_Encode_Property(
|
|||||||
* by a call to apdu_set_confirmed_handler().
|
* by a call to apdu_set_confirmed_handler().
|
||||||
* This handler builds a response packet, which is
|
* This handler builds a response packet, which is
|
||||||
* - an Abort if
|
* - an Abort if
|
||||||
* - the message is segmented
|
* - the message is segmented and SEGMENTATION_NONE
|
||||||
* - if decoding fails
|
* - if decoding fails
|
||||||
* - if the response would be too large
|
* - if the response would be too large
|
||||||
* - the result from each included read request, if it succeeds
|
* - the result from each included read request, if it succeeds
|
||||||
@@ -220,6 +234,14 @@ void handler_read_property_multiple(
|
|||||||
int apdu_len = 0;
|
int apdu_len = 0;
|
||||||
int npdu_len = 0;
|
int npdu_len = 0;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
int max_resp = 0;
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
BACNET_APDU_FIXED_HEADER apdu_fixed_header;
|
||||||
|
int apdu_header_len = 3;
|
||||||
|
#endif
|
||||||
|
#ifdef BACNET_RPM_PROPERTY_BUFFER_USE_CSTACK
|
||||||
|
uint8_t RPM_Prop_Buffer[MAX_ASDU - MAX_NPDU] = { 0 };
|
||||||
|
#endif
|
||||||
|
|
||||||
if (service_data) {
|
if (service_data) {
|
||||||
datalink_get_my_address(&my_address);
|
datalink_get_my_address(&my_address);
|
||||||
@@ -230,10 +252,12 @@ void handler_read_property_multiple(
|
|||||||
rpmdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
rpmdata.error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
||||||
error = BACNET_STATUS_REJECT;
|
error = BACNET_STATUS_REJECT;
|
||||||
debug_print("RPM: Missing Required Parameter. Sending Reject!\n");
|
debug_print("RPM: Missing Required Parameter. Sending Reject!\n");
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
} else if (service_data->segmented_message) {
|
} else if (service_data->segmented_message) {
|
||||||
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
rpmdata.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
debug_print("RPM: Segmented message. Sending Abort!\r\n");
|
debug_print("RPM: Segmented message. Sending Abort!\r\n");
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* decode apdu request & encode apdu reply
|
/* decode apdu request & encode apdu reply
|
||||||
encode complex ack, invoke id, service choice */
|
encode complex ack, invoke id, service choice */
|
||||||
@@ -279,14 +303,14 @@ void handler_read_property_multiple(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Stick this object id into the reply - if it will fit */
|
/* Stick this object id into the reply - if it will fit */
|
||||||
len = rpm_ack_encode_apdu_object_begin(&Temp_Buf[0], &rpmdata);
|
len = rpm_ack_encode_apdu_object_begin(
|
||||||
|
&RPM_Prop_Buffer[0], &rpmdata);
|
||||||
copy_len = memcopy(
|
copy_len = memcopy(
|
||||||
&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0], apdu_len,
|
&Handler_Transmit_Buffer[npdu_len], &RPM_Prop_Buffer[0],
|
||||||
len, MAX_APDU);
|
apdu_len, len, sizeof(RPM_Prop_Buffer));
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
debug_print("RPM: Response too big!\n");
|
debug_print("RPM: Response too big!\n");
|
||||||
rpmdata.error_code =
|
rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
berror = true;
|
berror = true;
|
||||||
break;
|
break;
|
||||||
@@ -321,7 +345,9 @@ void handler_read_property_multiple(
|
|||||||
rpmdata.object_type, rpmdata.object_instance)) {
|
rpmdata.object_type, rpmdata.object_instance)) {
|
||||||
len = RPM_Encode_Property(
|
len = RPM_Encode_Property(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
(uint16_t)apdu_len,
|
||||||
|
sizeof(Handler_Transmit_Buffer) - npdu_len,
|
||||||
|
&rpmdata);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
} else {
|
} else {
|
||||||
@@ -336,18 +362,18 @@ void handler_read_property_multiple(
|
|||||||
/* No array index options for this special property.
|
/* No array index options for this special property.
|
||||||
Encode error for this object property response */
|
Encode error for this object property response */
|
||||||
len = rpm_ack_encode_apdu_object_property(
|
len = rpm_ack_encode_apdu_object_property(
|
||||||
&Temp_Buf[0], rpmdata.object_property,
|
&RPM_Prop_Buffer[0], rpmdata.object_property,
|
||||||
rpmdata.array_index);
|
rpmdata.array_index);
|
||||||
|
|
||||||
copy_len = memcopy(
|
copy_len = memcopy(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
&RPM_Prop_Buffer[0], apdu_len, len,
|
||||||
|
sizeof(RPM_Prop_Buffer));
|
||||||
|
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
debug_print(
|
debug_print(
|
||||||
"RPM: Too full to encode property!\n");
|
"RPM: Too full to encode property!\n");
|
||||||
rpmdata.error_code =
|
rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
/* The berror flag ensures that
|
/* The berror flag ensures that
|
||||||
both loops will be broken! */
|
both loops will be broken! */
|
||||||
@@ -357,17 +383,17 @@ void handler_read_property_multiple(
|
|||||||
|
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
len = rpm_ack_encode_apdu_object_property_error(
|
len = rpm_ack_encode_apdu_object_property_error(
|
||||||
&Temp_Buf[0], ERROR_CLASS_PROPERTY,
|
&RPM_Prop_Buffer[0], ERROR_CLASS_PROPERTY,
|
||||||
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
|
ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY);
|
||||||
|
|
||||||
copy_len = memcopy(
|
copy_len = memcopy(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
&Temp_Buf[0], apdu_len, len, MAX_APDU);
|
&RPM_Prop_Buffer[0], apdu_len, len,
|
||||||
|
sizeof(RPM_Prop_Buffer));
|
||||||
|
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
debug_print("RPM: Too full to encode error!\n");
|
debug_print("RPM: Too full to encode error!\n");
|
||||||
rpmdata.error_code =
|
rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
/* The berror flag ensures that
|
/* The berror flag ensures that
|
||||||
both loops will be broken! */
|
both loops will be broken! */
|
||||||
@@ -396,7 +422,10 @@ void handler_read_property_multiple(
|
|||||||
rpmdata.object_instance)) {
|
rpmdata.object_instance)) {
|
||||||
len = RPM_Encode_Property(
|
len = RPM_Encode_Property(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
(uint16_t)apdu_len,
|
||||||
|
sizeof(Handler_Transmit_Buffer) -
|
||||||
|
npdu_len,
|
||||||
|
&rpmdata);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
} else {
|
} else {
|
||||||
@@ -418,7 +447,10 @@ void handler_read_property_multiple(
|
|||||||
special_object_property, index);
|
special_object_property, index);
|
||||||
len = RPM_Encode_Property(
|
len = RPM_Encode_Property(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
(uint16_t)apdu_len,
|
||||||
|
sizeof(Handler_Transmit_Buffer) -
|
||||||
|
npdu_len,
|
||||||
|
&rpmdata);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
} else {
|
} else {
|
||||||
@@ -437,7 +469,9 @@ void handler_read_property_multiple(
|
|||||||
/* handle an individual property */
|
/* handle an individual property */
|
||||||
len = RPM_Encode_Property(
|
len = RPM_Encode_Property(
|
||||||
&Handler_Transmit_Buffer[npdu_len],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
(uint16_t)apdu_len, MAX_APDU, &rpmdata);
|
(uint16_t)apdu_len,
|
||||||
|
sizeof(Handler_Transmit_Buffer) - npdu_len,
|
||||||
|
&rpmdata);
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
apdu_len += len;
|
apdu_len += len;
|
||||||
} else {
|
} else {
|
||||||
@@ -456,15 +490,16 @@ void handler_read_property_multiple(
|
|||||||
/* Reached end of property list so cap the result list
|
/* Reached end of property list so cap the result list
|
||||||
*/
|
*/
|
||||||
decode_len++;
|
decode_len++;
|
||||||
len = rpm_ack_encode_apdu_object_end(&Temp_Buf[0]);
|
len =
|
||||||
|
rpm_ack_encode_apdu_object_end(&RPM_Prop_Buffer[0]);
|
||||||
copy_len = memcopy(
|
copy_len = memcopy(
|
||||||
&Handler_Transmit_Buffer[npdu_len], &Temp_Buf[0],
|
&Handler_Transmit_Buffer[npdu_len],
|
||||||
apdu_len, len, MAX_APDU);
|
&RPM_Prop_Buffer[0], apdu_len, len,
|
||||||
|
sizeof(Handler_Transmit_Buffer) - npdu_len);
|
||||||
if (copy_len == 0) {
|
if (copy_len == 0) {
|
||||||
debug_print(
|
debug_print(
|
||||||
"RPM: Too full to encode object end!\n");
|
"RPM: Too full to encode object end!\n");
|
||||||
rpmdata.error_code =
|
rpmdata.error_code = BACNET_RPM_BUFFER_OVERFLOW;
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
/* The berror flag ensures that
|
/* The berror flag ensures that
|
||||||
both loops will be broken! */
|
both loops will be broken! */
|
||||||
@@ -487,12 +522,36 @@ void handler_read_property_multiple(
|
|||||||
}
|
}
|
||||||
/* If not having an error so far, check the remaining space. */
|
/* If not having an error so far, check the remaining space. */
|
||||||
if (!berror) {
|
if (!berror) {
|
||||||
if (apdu_len > service_data->max_resp) {
|
max_resp = min(service_data->max_resp, MAX_APDU);
|
||||||
|
if (apdu_len > max_resp) {
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
if (service_data->segmented_response_accepted) {
|
||||||
|
apdu_init_fixed_header(
|
||||||
|
&apdu_fixed_header, PDU_TYPE_COMPLEX_ACK,
|
||||||
|
service_data->invoke_id,
|
||||||
|
SERVICE_CONFIRMED_READ_PROP_MULTIPLE,
|
||||||
|
service_data->max_resp);
|
||||||
|
|
||||||
|
npdu_encode_npdu_data(
|
||||||
|
&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||||
|
npdu_len = npdu_encode_pdu(
|
||||||
|
&Handler_Transmit_Buffer[0], src, &my_address,
|
||||||
|
&npdu_data);
|
||||||
|
|
||||||
|
tsm_set_complexack_transaction(
|
||||||
|
src, &npdu_data, &apdu_fixed_header, service_data,
|
||||||
|
&Handler_Transmit_Buffer
|
||||||
|
[npdu_len + apdu_header_len],
|
||||||
|
(apdu_len - apdu_header_len));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
/* too big for the sender - send an abort */
|
/* too big for the sender - send an abort */
|
||||||
rpmdata.error_code =
|
rpmdata.error_code =
|
||||||
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
error = BACNET_STATUS_ABORT;
|
error = BACNET_STATUS_ABORT;
|
||||||
debug_print("RPM: Message too large. Sending Abort!\n");
|
debug_print("RPM: Message too large. Sending Abort!\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,13 +107,16 @@ void handler_write_property(
|
|||||||
REJECT_REASON_MISSING_REQUIRED_PARAMETER);
|
REJECT_REASON_MISSING_REQUIRED_PARAMETER);
|
||||||
debug_print("WP: Missing Required Parameter. Sending Reject!\n");
|
debug_print("WP: Missing Required Parameter. Sending Reject!\n");
|
||||||
bcontinue = false;
|
bcontinue = false;
|
||||||
} else if (service_data->segmented_message) {
|
}
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
|
else if (service_data->segmented_message) {
|
||||||
len = abort_encode_apdu(
|
len = abort_encode_apdu(
|
||||||
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
&Handler_Transmit_Buffer[pdu_len], service_data->invoke_id,
|
||||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, true);
|
||||||
debug_print("WP: Segmented message. Sending Abort!\n");
|
debug_print("WP: Segmented message. Sending Abort!\n");
|
||||||
bcontinue = false;
|
bcontinue = false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (bcontinue) {
|
if (bcontinue) {
|
||||||
/* decode the service request only */
|
/* decode the service request only */
|
||||||
len = wp_decode_service_request(service_request, service_len, &wp_data);
|
len = wp_decode_service_request(service_request, service_len, &wp_data);
|
||||||
|
|||||||
@@ -141,11 +141,13 @@ void handler_write_property_multiple(
|
|||||||
len = BACNET_STATUS_REJECT;
|
len = BACNET_STATUS_REJECT;
|
||||||
debug_print("WPM: Missing Required Parameter. "
|
debug_print("WPM: Missing Required Parameter. "
|
||||||
"Sending Reject!\n");
|
"Sending Reject!\n");
|
||||||
|
#if !BACNET_SEGMENTATION_ENABLED
|
||||||
} else if (service_data->segmented_message) {
|
} else if (service_data->segmented_message) {
|
||||||
wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
wp_data.error_code = ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
|
||||||
len = BACNET_STATUS_ABORT;
|
len = BACNET_STATUS_ABORT;
|
||||||
debug_print("WPM: Segmented message. "
|
debug_print("WPM: Segmented message. "
|
||||||
"Sending Abort!\n");
|
"Sending Abort!\n");
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
/* first time - detect malformed request before writing any data */
|
/* first time - detect malformed request before writing any data */
|
||||||
len = write_property_multiple_decode(
|
len = write_property_multiple_decode(
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+1243
-63
File diff suppressed because it is too large
Load Diff
+102
-11
@@ -15,6 +15,9 @@
|
|||||||
#include "bacnet/bacdef.h"
|
#include "bacnet/bacdef.h"
|
||||||
/* BACnet Stack API */
|
/* BACnet Stack API */
|
||||||
#include "bacnet/npdu.h"
|
#include "bacnet/npdu.h"
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
#include "bacnet/apdu.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
/* note: TSM functionality is optional - only needed if we are
|
/* note: TSM functionality is optional - only needed if we are
|
||||||
doing client requests */
|
doing client requests */
|
||||||
@@ -24,7 +27,7 @@ extern "C" {
|
|||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
/* FIXME: modify basic service handlers to use TSM rather than this buffer! */
|
/* FIXME: modify basic service handlers to use TSM rather than this buffer! */
|
||||||
BACNET_STACK_EXPORT extern uint8_t Handler_Transmit_Buffer[MAX_PDU];
|
BACNET_STACK_EXPORT extern uint8_t Handler_Transmit_Buffer[MAX_ASDU];
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@@ -37,31 +40,47 @@ typedef enum {
|
|||||||
TSM_STATE_IDLE,
|
TSM_STATE_IDLE,
|
||||||
TSM_STATE_AWAIT_CONFIRMATION,
|
TSM_STATE_AWAIT_CONFIRMATION,
|
||||||
TSM_STATE_AWAIT_RESPONSE,
|
TSM_STATE_AWAIT_RESPONSE,
|
||||||
TSM_STATE_SEGMENTED_REQUEST,
|
TSM_STATE_SEGMENTED_REQUEST_SERVER,
|
||||||
TSM_STATE_SEGMENTED_CONFIRMATION
|
TSM_STATE_SEGMENTED_CONFIRMATION,
|
||||||
|
TSM_STATE_SEGMENTED_RESPONSE_SERVER
|
||||||
} BACNET_TSM_STATE;
|
} BACNET_TSM_STATE;
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/* Indirect data state : */
|
||||||
|
typedef struct BACnet_TSM_Indirect_Data {
|
||||||
|
/* the address we received data from */
|
||||||
|
BACNET_ADDRESS PeerAddress;
|
||||||
|
/* the peer unique id */
|
||||||
|
uint8_t PeerInvokeID;
|
||||||
|
/* the unique id to use within our internal states.
|
||||||
|
zero means : "unused slot". */
|
||||||
|
uint8_t InternalInvokeID;
|
||||||
|
} BACNET_TSM_INDIRECT_DATA;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* 5.4.1 Variables And Parameters */
|
/* 5.4.1 Variables And Parameters */
|
||||||
/* The following variables are defined for each instance of */
|
/* The following variables are defined for each instance of */
|
||||||
/* Transaction State Machine: */
|
/* Transaction State Machine: */
|
||||||
typedef struct BACnet_TSM_Data {
|
typedef struct BACnet_TSM_Data {
|
||||||
/* used to count APDU retries */
|
/* used to count APDU retries */
|
||||||
uint8_t RetryCount;
|
uint8_t RetryCount;
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
/* used to count segment retries */
|
/* used to count segment retries */
|
||||||
/*uint8_t SegmentRetryCount; */
|
uint8_t SegmentRetryCount;
|
||||||
/* used to control APDU retries and the acceptance of server replies */
|
/* used to control APDU retries and the acceptance of server replies */
|
||||||
/*bool SentAllSegments; */
|
bool SentAllSegments;
|
||||||
/* stores the sequence number of the last segment received in order */
|
/* stores the sequence number of the last segment received in order */
|
||||||
/*uint8_t LastSequenceNumber; */
|
uint8_t LastSequenceNumber;
|
||||||
/* stores the sequence number of the first segment of */
|
/* stores the sequence number of the first segment of */
|
||||||
/* a sequence of segments that fill a window */
|
/* a sequence of segments that fill a window */
|
||||||
/*uint8_t InitialSequenceNumber; */
|
uint8_t InitialSequenceNumber;
|
||||||
/* stores the current window size */
|
/* stores the current window size */
|
||||||
/*uint8_t ActualWindowSize; */
|
uint8_t ActualWindowSize;
|
||||||
/* stores the window size proposed by the segment sender */
|
/* stores the window size proposed by the segment sender */
|
||||||
/*uint8_t ProposedWindowSize; */
|
uint8_t ProposedWindowSize;
|
||||||
/* used to perform timeout on PDU segments */
|
/* used to perform timeout on PDU segments */
|
||||||
/*uint8_t SegmentTimer; */
|
uint16_t SegmentTimer;
|
||||||
|
#endif
|
||||||
/* used to perform timeout on Confirmed Requests */
|
/* used to perform timeout on Confirmed Requests */
|
||||||
/* in milliseconds */
|
/* in milliseconds */
|
||||||
uint16_t RequestTimer;
|
uint16_t RequestTimer;
|
||||||
@@ -73,9 +92,28 @@ typedef struct BACnet_TSM_Data {
|
|||||||
BACNET_ADDRESS dest;
|
BACNET_ADDRESS dest;
|
||||||
/* the network layer info */
|
/* the network layer info */
|
||||||
BACNET_NPDU_DATA npdu_data;
|
BACNET_NPDU_DATA npdu_data;
|
||||||
|
unsigned apdu_len;
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/* APDU header information */
|
||||||
|
BACNET_APDU_FIXED_HEADER apdu_fixed_header;
|
||||||
|
/* calculated max APDU length / packet */
|
||||||
|
uint32_t apdu_maximum_length;
|
||||||
|
/* calculated max APDU length / total */
|
||||||
|
uint32_t maximum_transmittable_length;
|
||||||
|
/* Multiple APDU segments blob memorized here */
|
||||||
|
uint8_t *apdu_blob;
|
||||||
|
/* Size of allocated Multiple APDU segments blob */
|
||||||
|
uint32_t apdu_blob_allocated;
|
||||||
|
/* Size of data within the multiple APDU segments blob */
|
||||||
|
uint32_t apdu_blob_size;
|
||||||
|
/* Count received segments (prevents D.O.S.) */
|
||||||
|
uint32_t ReceivedSegmentsCount;
|
||||||
|
/* copy of the APDU, should we need to send it again */
|
||||||
|
uint8_t *apdu;
|
||||||
|
#else
|
||||||
/* copy of the APDU, should we need to send it again */
|
/* copy of the APDU, should we need to send it again */
|
||||||
uint8_t apdu[MAX_PDU];
|
uint8_t apdu[MAX_PDU];
|
||||||
unsigned apdu_len;
|
#endif
|
||||||
} BACNET_TSM_DATA;
|
} BACNET_TSM_DATA;
|
||||||
|
|
||||||
typedef void (*tsm_timeout_function)(uint8_t invoke_id);
|
typedef void (*tsm_timeout_function)(uint8_t invoke_id);
|
||||||
@@ -123,6 +161,59 @@ bool tsm_invoke_id_free(uint8_t invokeID);
|
|||||||
BACNET_STACK_EXPORT
|
BACNET_STACK_EXPORT
|
||||||
bool tsm_invoke_id_failed(uint8_t invokeID);
|
bool tsm_invoke_id_failed(uint8_t invokeID);
|
||||||
|
|
||||||
|
#if BACNET_SEGMENTATION_ENABLED
|
||||||
|
/** Clear TSM Peer data */
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void tsm_clear_peer_id(uint8_t InternalInvokeID);
|
||||||
|
|
||||||
|
/* frees the invokeID and sets its state to IDLE */
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void tsm_free_invoke_id_check(
|
||||||
|
uint8_t invokeID, BACNET_ADDRESS *peer_address, bool cleanup);
|
||||||
|
|
||||||
|
/* Associates a Peer address and invoke ID with our TSM */
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
uint8_t tsm_get_peer_id(BACNET_ADDRESS *src, uint8_t invokeID);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool tsm_set_segmented_confirmed_service_received(
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *service_data,
|
||||||
|
uint8_t *internal_invoke_id,
|
||||||
|
uint8_t **pservice_request, /* IN/OUT */
|
||||||
|
uint16_t *pservice_request_len /* IN/OUT */
|
||||||
|
);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
int tsm_set_complexack_transaction(
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_NPDU_DATA *npdu_data,
|
||||||
|
BACNET_APDU_FIXED_HEADER *apdu_fixed_header,
|
||||||
|
BACNET_CONFIRMED_SERVICE_DATA *confirmed_service_data,
|
||||||
|
uint8_t *pdu,
|
||||||
|
uint32_t pdu_len);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void tsm_segmentack_received(
|
||||||
|
uint8_t invoke_id,
|
||||||
|
uint8_t sequence_number,
|
||||||
|
uint8_t actual_window_size,
|
||||||
|
bool nak,
|
||||||
|
bool server,
|
||||||
|
BACNET_ADDRESS *src);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
bool tsm_is_invalid_apdu_in_this_state(
|
||||||
|
BACNET_ADDRESS *src, BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void tsm_abort_pdu_send(
|
||||||
|
uint8_t invoke_id, BACNET_ADDRESS *dest, uint8_t reason, bool server);
|
||||||
|
|
||||||
|
BACNET_STACK_EXPORT
|
||||||
|
void tsm_free_invoke_id_segmentation(BACNET_ADDRESS *src, uint8_t invoke_id);
|
||||||
|
|
||||||
|
#endif
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|||||||
@@ -164,6 +164,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 */
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*####COPYRIGHTBEGIN####
|
||||||
|
-------------------------------------------
|
||||||
|
Copyright (C) 2010 Julien Bennet
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to:
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or
|
||||||
|
use macros or inline functions from this file, or you compile
|
||||||
|
this file and link it with other works to produce a work based
|
||||||
|
on this file, this file does not by itself cause the resulting
|
||||||
|
work to be covered by the GNU General Public License. However
|
||||||
|
the source code for this file must still be made available in
|
||||||
|
accordance with section (3) of the GNU General Public License.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work
|
||||||
|
based on this file might be covered by the GNU General Public
|
||||||
|
License.
|
||||||
|
-------------------------------------------
|
||||||
|
####COPYRIGHTEND####*/
|
||||||
|
#include "segmentack.h"
|
||||||
|
|
||||||
|
/** Method to encode the segment ack .
|
||||||
|
*
|
||||||
|
* @param apdu[in] Pointer to the buffer for encoding.
|
||||||
|
* @param negativeack[in] Acknowedlegment 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 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[in] Invoke Id of the request.
|
||||||
|
* @param sequence_number[in] Sequence number of the segment received.
|
||||||
|
* @param actual_window_size[in] 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
/*####COPYRIGHTBEGIN####
|
||||||
|
-------------------------------------------
|
||||||
|
Copyright (C) 2010 Julien Bennet
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU General Public License
|
||||||
|
as published by the Free Software Foundation; either version 2
|
||||||
|
of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program; if not, write to:
|
||||||
|
The Free Software Foundation, Inc.
|
||||||
|
59 Temple Place - Suite 330
|
||||||
|
Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
|
As a special exception, if other files instantiate templates or
|
||||||
|
use macros or inline functions from this file, or you compile
|
||||||
|
this file and link it with other works to produce a work based
|
||||||
|
on this file, this file does not by itself cause the resulting
|
||||||
|
work to be covered by the GNU General Public License. However
|
||||||
|
the source code for this file must still be made available in
|
||||||
|
accordance with section (3) of the GNU General Public License.
|
||||||
|
|
||||||
|
This exception does not invalidate any other reasons why a work
|
||||||
|
based on this file might be covered by the GNU General Public
|
||||||
|
License.
|
||||||
|
-------------------------------------------
|
||||||
|
####COPYRIGHTEND####*/
|
||||||
|
#ifndef BACNET_SEGMENT_ACK_H
|
||||||
|
#define BACNET_SEGMENT_ACK_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "bacenum.h"
|
||||||
|
#include "bacdcode.h"
|
||||||
|
#include "bacdef.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 /* SEGMENT_ACK_H */
|
||||||
Reference in New Issue
Block a user