Feature/writepropertymultiple error handler (#289)

* Added extended BACnet Error PDU handling for WPM

* Added BACnetObjectPropertyReference to bacapp module.

* Added unit testing for BACnetObjectPropertyReference

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2022-06-03 15:47:30 -05:00
committed by GitHub
parent f147283293
commit 6a8adcce3d
14 changed files with 642 additions and 88 deletions
+17 -1
View File
@@ -99,6 +99,21 @@ static void MyErrorHandler(BACNET_ADDRESS *src,
/* Error_Detected = true; */ /* Error_Detected = true; */
} }
/* complex error reply function */
static void MyPrivateTransferErrorHandler(
BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t service_choice,
uint8_t * service_request,
uint16_t service_len)
{
(void)src;
(void)invoke_id;
(void)service_choice;
(void)service_request;
(void)service_len;
}
static void MyAbortHandler( static void MyAbortHandler(
BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server) BACNET_ADDRESS *src, uint8_t invoke_id, uint8_t abort_reason, bool server)
{ {
@@ -148,7 +163,8 @@ static void Init_Service_Handlers(void)
/* handle any errors coming back */ /* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler); apdu_set_complex_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER,
MyPrivateTransferErrorHandler);
apdu_set_abort_handler(MyAbortHandler); apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler); apdu_set_reject_handler(MyRejectHandler);
} }
-1
View File
@@ -131,7 +131,6 @@ static void Init_Service_Handlers(void)
/* handle any errors coming back */ /* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler);
apdu_set_error_handler(SERVICE_CONFIRMED_PRIVATE_TRANSFER, MyErrorHandler);
apdu_set_abort_handler(MyAbortHandler); apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler); apdu_set_reject_handler(MyRejectHandler);
} }
+22 -8
View File
@@ -70,18 +70,30 @@ static bool Error_Detected = false;
/* Used for verbose */ /* Used for verbose */
static bool Verbose = false; static bool Verbose = false;
static void MyErrorHandler(BACNET_ADDRESS *src, static void MyWritePropertyMultipleErrorHandler(
BACNET_ADDRESS * src,
uint8_t invoke_id, uint8_t invoke_id,
BACNET_ERROR_CLASS error_class, uint8_t service_choice,
BACNET_ERROR_CODE error_code) uint8_t * service_request,
uint16_t service_len)
{ {
int len = 0;
BACNET_WRITE_PROPERTY_DATA wp_data = { 0 };
(void)service_choice;
if (address_match(&Target_Address, src) && if (address_match(&Target_Address, src) &&
(invoke_id == Request_Invoke_ID)) { (invoke_id == Request_Invoke_ID)) {
printf("BACnet Error: %s: %s\n", len = wpm_error_ack_decode_apdu(service_request, service_len, &wp_data);
bactext_error_class_name((int)error_class), if (len > 0) {
bactext_error_code_name((int)error_code)); printf("BACnet Error: %s: %s\n",
bactext_error_class_name((int)wp_data.error_class),
bactext_error_code_name((int)wp_data.error_code));
printf("BACnet Error: %s %u: %s\n",
bactext_object_type_name((int)wp_data.object_type),
(unsigned)wp_data.object_instance,
bactext_property_name((int)wp_data.object_property));
}
Error_Detected = true; Error_Detected = true;
/* FIXME: WPM error has extra data for first failed write. */
} }
} }
@@ -109,6 +121,7 @@ static void MyRejectHandler(
} }
} }
static void MyWritePropertyMultipleSimpleAckHandler( static void MyWritePropertyMultipleSimpleAckHandler(
BACNET_ADDRESS *src, uint8_t invoke_id) BACNET_ADDRESS *src, uint8_t invoke_id)
{ {
@@ -136,7 +149,8 @@ static void Init_Service_Handlers(void)
apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, apdu_set_confirmed_simple_ack_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,
MyWritePropertyMultipleSimpleAckHandler); MyWritePropertyMultipleSimpleAckHandler);
/* handle any errors coming back */ /* handle any errors coming back */
apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, MyErrorHandler); apdu_set_complex_error_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE,
MyWritePropertyMultipleErrorHandler);
apdu_set_abort_handler(MyAbortHandler); apdu_set_abort_handler(MyAbortHandler);
apdu_set_reject_handler(MyRejectHandler); apdu_set_reject_handler(MyRejectHandler);
} }
+34
View File
@@ -158,6 +158,7 @@ int bacapp_encode_application_data(
apdu, &value->type.Lighting_Command); apdu, &value->type.Lighting_Command);
break; break;
case BACNET_APPLICATION_TAG_HOST_N_PORT: case BACNET_APPLICATION_TAG_HOST_N_PORT:
/* BACnetHostNPort */
apdu_len = host_n_port_encode(apdu, apdu_len = host_n_port_encode(apdu,
&value->type.Host_Address); &value->type.Host_Address);
break; break;
@@ -166,6 +167,16 @@ int bacapp_encode_application_data(
apdu_len = bacapp_encode_device_obj_property_ref( apdu_len = bacapp_encode_device_obj_property_ref(
apdu, &value->type.Device_Object_Property_Reference); apdu, &value->type.Device_Object_Property_Reference);
break; break;
case BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE:
/* BACnetDeviceObjectReference */
apdu_len = bacapp_encode_device_obj_ref(
apdu, &value->type.Device_Object_Reference);
break;
case BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE:
/* BACnetObjectPropertyReference */
apdu_len = bacapp_encode_obj_property_ref(
apdu, &value->type.Object_Property_Reference);
break;
#endif #endif
default: default:
break; break;
@@ -289,6 +300,17 @@ int bacapp_decode_data(uint8_t *apdu,
len = bacapp_decode_device_obj_property_ref( len = bacapp_decode_device_obj_property_ref(
apdu, &value->type.Device_Object_Property_Reference); apdu, &value->type.Device_Object_Property_Reference);
break; break;
case BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE:
/* BACnetDeviceObjectReference */
len = bacapp_decode_device_obj_ref(
apdu, &value->type.Device_Object_Reference);
break;
case BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE:
/* BACnetObjectPropertyReference */
len = bacapp_decode_obj_property_ref(
apdu, len_value_type,
&value->type.Object_Property_Reference);
break;
#endif #endif
default: default:
break; break;
@@ -589,6 +611,18 @@ int bacapp_encode_context_data_value(uint8_t *apdu,
apdu, context_tag_number, apdu, context_tag_number,
&value->type.Device_Object_Property_Reference); &value->type.Device_Object_Property_Reference);
break; break;
case BACNET_APPLICATION_TAG_DEVICE_OBJECT_REFERENCE:
/* BACnetDeviceObjectReference */
apdu_len = bacapp_encode_context_device_obj_ref(
apdu, context_tag_number,
&value->type.Device_Object_Reference);
break;
case BACNET_APPLICATION_TAG_OBJECT_PROPERTY_REFERENCE:
/* BACnetObjectPropertyReference */
apdu_len = bacapp_encode_context_obj_property_ref(
apdu, context_tag_number,
&value->type.Object_Property_Reference);
break;
#endif #endif
default: default:
break; break;
+4
View File
@@ -84,6 +84,10 @@ typedef struct BACnet_Application_Data_Value {
BACNET_HOST_N_PORT Host_Address; BACNET_HOST_N_PORT Host_Address;
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE
Device_Object_Property_Reference; Device_Object_Property_Reference;
BACNET_DEVICE_OBJECT_REFERENCE
Device_Object_Reference;
BACNET_OBJECT_PROPERTY_REFERENCE
Object_Property_Reference;
#endif #endif
} type; } type;
/* simple linked list if needed */ /* simple linked list if needed */
+214
View File
@@ -1,6 +1,7 @@
/*####COPYRIGHTBEGIN#### /*####COPYRIGHTBEGIN####
------------------------------------------- -------------------------------------------
Copyright (C) 2008 John Minack Copyright (C) 2008 John Minack
Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License modify it under the terms of the GNU General Public License
@@ -400,3 +401,216 @@ int bacapp_decode_context_device_obj_ref(
} }
return len; return len;
} }
/**
* @brief Encode a BACnetObjectPropertyReference
*
* BACnetObjectPropertyReference ::= SEQUENCE {
* object-identifier [0] BACnetObjectIdentifier,
* property-identifier [1] BACnetPropertyIdentifier,
* property-array-index [2] Unsigned OPTIONAL
* -- used only with array datatype
* -- if omitted with an array the entire array is referenced
* }
*
* @param apdu - the APDU buffer, or NULL for length
* @param reference - BACnetObjectPropertyReference
* @return length of the APDU buffer
*/
int bacapp_encode_obj_property_ref(uint8_t *apdu,
BACNET_OBJECT_PROPERTY_REFERENCE *reference)
{
int len = 0;
uint8_t *apdu_offset = NULL;
int apdu_len = 0;
if (!reference) {
return 0;
}
if (reference->object_identifier.type == OBJECT_NONE) {
return 0;
}
if (apdu) {
apdu_offset = apdu;
}
len = encode_context_object_id(apdu_offset, 0,
reference->object_identifier.type,
reference->object_identifier.instance);
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = encode_context_enumerated(
apdu_offset, 1, reference->property_identifier);
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
if (reference->property_array_index != BACNET_ARRAY_ALL) {
len = encode_context_unsigned(
apdu_offset, 2, reference->property_array_index);
apdu_len += len;
}
return apdu_len;
}
/**
* @brief Encode the BACnetObjectPropertyReference as Context Tagged
* @param apdu - the APDU buffer
* @param tag_number - context tag number to be encoded
* @param reference - BACnetObjectPropertyReference to encode
* @return length of the APDU buffer
*/
int bacapp_encode_context_obj_property_ref(uint8_t *apdu,
uint8_t tag_number,
BACNET_OBJECT_PROPERTY_REFERENCE *reference)
{
int len = 0;
int apdu_len = 0;
uint8_t *apdu_offset = NULL;
if (reference && (reference->object_identifier.type == OBJECT_NONE)) {
return 0;
}
if (apdu) {
apdu_offset = apdu;
}
len = encode_opening_tag(apdu_offset, tag_number);
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = bacapp_encode_obj_property_ref(apdu_offset, reference);
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
}
len = encode_closing_tag(apdu_offset, tag_number);
apdu_len += len;
return apdu_len;
}
/**
* @brief Decode a BACnetObjectPropertyReference
*
* BACnetObjectPropertyReference ::= SEQUENCE {
* object-identifier [0] BACnetObjectIdentifier,
* property-identifier [1] BACnetPropertyIdentifier,
* property-array-index [2] Unsigned OPTIONAL
* -- used only with array datatype
* -- if omitted with an array the entire array is referenced
* }
*
* @param apdu - the APDU buffer
* @param apdu_len_max - the APDU buffer length
* @param reference - BACnetObjectPropertyReference to decode into
* @return length of the APDU buffer decoded, or 0 if failed to decode
*/
int bacapp_decode_obj_property_ref(uint8_t *apdu,
uint16_t apdu_len_max,
BACNET_OBJECT_PROPERTY_REFERENCE *reference)
{
int apdu_len = 0;
int len = 0;
BACNET_OBJECT_ID object_identifier;
BACNET_PROPERTY_ID property_identifier;
BACNET_UNSIGNED_INTEGER unsigned_value;
if (apdu && (apdu_len_max > 0)) {
/* object-identifier [0] BACnetObjectIdentifier */
len = bacnet_object_id_context_decode(&apdu[apdu_len],
apdu_len_max - apdu_len, 0, &object_identifier.type,
&object_identifier.instance);
if (len > 0) {
apdu_len += len;
} else if (len <= 0) {
return 0;
}
/* property-identifier [1] BACnetPropertyIdentifier */
len = bacnet_enumerated_context_decode(
&apdu[apdu_len], apdu_len_max - apdu_len, 1, &property_identifier);
if (len > 0) {
apdu_len += len;
} else if (len <= 0) {
return 0;
}
if (reference) {
reference->object_identifier.type = object_identifier.type;
reference->object_identifier.instance = object_identifier.instance;
reference->property_identifier = property_identifier;
reference->property_array_index = BACNET_ARRAY_ALL;
}
/* property-array-index [2] Unsigned OPTIONAL */
if (apdu_len_max > apdu_len) {
if (decode_is_context_tag(&apdu[apdu_len], 2)) {
len = bacnet_unsigned_context_decode(
&apdu[apdu_len], apdu_len_max - apdu_len, 2,
&unsigned_value);
if (len > 0) {
apdu_len += len;
if (unsigned_value > UINT32_MAX) {
return 0;
}
if (reference) {
reference->property_array_index = unsigned_value;
}
} else if (len <= 0) {
return 0;
}
}
}
}
return apdu_len;
}
/**
* Decode the context object property reference. Check for
* an opening tag and a closing tag as well.
*
* @param apdu Pointer to the buffer containing the data to decode.
* @param apdu_len_max - the APDU buffer length
* @param tag_number Tag number
* @param value Pointer to the context device object reference,
* that shall be decoded.
*
* @return Bytes decoded or BACNET_STATUS_ERROR on failure.
*/
int bacapp_decode_context_obj_property_ref(
uint8_t * apdu,
uint16_t apdu_len_max,
uint8_t tag_number,
BACNET_OBJECT_PROPERTY_REFERENCE * value)
{
int len = 0;
int apdu_len = 0;
if (apdu_len_max == 0) {
return BACNET_STATUS_ERROR;
}
if (decode_is_opening_tag_number(&apdu[apdu_len], tag_number)) {
apdu_len++;
} else {
return BACNET_STATUS_ERROR;
}
len = bacapp_decode_obj_property_ref(&apdu[apdu_len],
apdu_len_max - apdu_len, value);
if (len == 0) {
return BACNET_STATUS_ERROR;
} else {
apdu_len += len;
}
if ((apdu_len_max - apdu_len) == 0) {
return BACNET_STATUS_ERROR;
}
if (decode_is_closing_tag_number(&apdu[apdu_len], tag_number)) {
apdu_len++;
} else {
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
+41 -2
View File
@@ -1,6 +1,7 @@
/************************************************************************** /**************************************************************************
* *
* Copyright (C) 2008 John Minack * Copyright (C) 2008 John Minack
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the * a copy of this software and associated documentation files (the
@@ -49,7 +50,22 @@ typedef struct BACnetDeviceObjectReference {
BACNET_OBJECT_ID objectIdentifier; BACNET_OBJECT_ID objectIdentifier;
} BACNET_DEVICE_OBJECT_REFERENCE; } BACNET_DEVICE_OBJECT_REFERENCE;
/**
* BACnetObjectPropertyReference ::= SEQUENCE {
* object-identifier [0] BACnetObjectIdentifier,
* property-identifier [1] BACnetPropertyIdentifier,
* property-array-index [2] Unsigned OPTIONAL
* -- used only with array datatype
* -- if omitted with an array the entire array is referenced
* }
*/
typedef struct BACnet_Object_Property_Reference {
/* note: use type = OBJECT_NONE for unused reference */
BACNET_OBJECT_ID object_identifier;
BACNET_PROPERTY_ID property_identifier;
/* optional array index - use BACNET_ARRAY_ALL when not used */
BACNET_ARRAY_INDEX property_array_index;
} BACNET_OBJECT_PROPERTY_REFERENCE;
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -77,7 +93,6 @@ extern "C" {
uint8_t tag_number, uint8_t tag_number,
BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value); BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE * value);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
int bacapp_encode_device_obj_ref( int bacapp_encode_device_obj_ref(
uint8_t * apdu, uint8_t * apdu,
@@ -100,6 +115,30 @@ extern "C" {
uint8_t tag_number, uint8_t tag_number,
BACNET_DEVICE_OBJECT_REFERENCE * value); BACNET_DEVICE_OBJECT_REFERENCE * value);
BACNET_STACK_EXPORT
int bacapp_encode_obj_property_ref(
uint8_t * apdu,
BACNET_OBJECT_PROPERTY_REFERENCE * value);
BACNET_STACK_EXPORT
int bacapp_encode_context_obj_property_ref(
uint8_t * apdu,
uint8_t tag_number,
BACNET_OBJECT_PROPERTY_REFERENCE * value);
BACNET_STACK_EXPORT
int bacapp_decode_obj_property_ref(
uint8_t * apdu,
uint16_t apdu_len_max,
BACNET_OBJECT_PROPERTY_REFERENCE * value);
BACNET_STACK_EXPORT
int bacapp_decode_context_obj_property_ref(
uint8_t * apdu,
uint16_t apdu_len_max,
uint8_t tag_number,
BACNET_OBJECT_PROPERTY_REFERENCE * value);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /* __cplusplus */ #endif /* __cplusplus */
+37 -11
View File
@@ -1417,17 +1417,21 @@ typedef enum {
PDU_TYPE_ABORT = 0x70 PDU_TYPE_ABORT = 0x70
} BACNET_PDU_TYPE; } BACNET_PDU_TYPE;
typedef enum { /* BACnetConfirmedServiceChoice ::= ENUMERATED */
typedef enum BACnet_Confirmed_Service_Choice {
/* Alarm and Event Services */ /* Alarm and Event Services */
SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM = 0, SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM = 0,
SERVICE_CONFIRMED_AUDIT_NOTIFICATION = 32,
SERVICE_CONFIRMED_COV_NOTIFICATION = 1, SERVICE_CONFIRMED_COV_NOTIFICATION = 1,
SERVICE_CONFIRMED_COV_NOTIFICATION_MULTIPLE = 31,
SERVICE_CONFIRMED_EVENT_NOTIFICATION = 2, SERVICE_CONFIRMED_EVENT_NOTIFICATION = 2,
SERVICE_CONFIRMED_GET_ALARM_SUMMARY = 3, SERVICE_CONFIRMED_GET_ALARM_SUMMARY = 3,
SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY = 4, SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY = 4,
SERVICE_CONFIRMED_GET_EVENT_INFORMATION = 29, SERVICE_CONFIRMED_GET_EVENT_INFORMATION = 29,
SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION = 27,
SERVICE_CONFIRMED_SUBSCRIBE_COV = 5, SERVICE_CONFIRMED_SUBSCRIBE_COV = 5,
SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY = 28, SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY = 28,
SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION = 27, SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY_MULTIPLE = 30,
/* File Access Services */ /* File Access Services */
SERVICE_CONFIRMED_ATOMIC_READ_FILE = 6, SERVICE_CONFIRMED_ATOMIC_READ_FILE = 6,
SERVICE_CONFIRMED_ATOMIC_WRITE_FILE = 7, SERVICE_CONFIRMED_ATOMIC_WRITE_FILE = 7,
@@ -1442,6 +1446,7 @@ typedef enum {
SERVICE_CONFIRMED_READ_RANGE = 26, SERVICE_CONFIRMED_READ_RANGE = 26,
SERVICE_CONFIRMED_WRITE_PROPERTY = 15, SERVICE_CONFIRMED_WRITE_PROPERTY = 15,
SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE = 16, SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE = 16,
SERVICE_CONFIRMED_AUDIT_LOG_QUERY = 33,
/* Remote Device Management Services */ /* Remote Device Management Services */
SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL = 17, SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL = 17,
SERVICE_CONFIRMED_PRIVATE_TRANSFER = 18, SERVICE_CONFIRMED_PRIVATE_TRANSFER = 18,
@@ -1455,14 +1460,21 @@ typedef enum {
SERVICE_CONFIRMED_AUTHENTICATE = 24, SERVICE_CONFIRMED_AUTHENTICATE = 24,
SERVICE_CONFIRMED_REQUEST_KEY = 25, SERVICE_CONFIRMED_REQUEST_KEY = 25,
/* Services added after 1995 */ /* Services added after 1995 */
/* readRange (26) see Object Access Services */ /* readRange [26] see Object Access Services */
/* lifeSafetyOperation (27) see Alarm and Event Services */ /* lifeSafetyOperation [27] see Alarm and Event Services */
/* subscribeCOVProperty (28) see Alarm and Event Services */ /* subscribeCOVProperty [28] see Alarm and Event Services */
/* getEventInformation (29) see Alarm and Event Services */ /* getEventInformation [29] see Alarm and Event Services */
/* Services added after 2012 */
/* subscribe-cov-property-multiple [30] see Alarm and Event Services */
/* confirmed-cov-notification-multiple [31] see Alarm and Event Services */
/* Services added after 2016 */
/* confirmed-audit-notification [32] see Alarm and Event Services */
/* audit-log-query [33] see Object Access Services */
MAX_BACNET_CONFIRMED_SERVICE = 30 MAX_BACNET_CONFIRMED_SERVICE = 30
} BACNET_CONFIRMED_SERVICE; } BACNET_CONFIRMED_SERVICE;
typedef enum { /* BACnetUnconfirmedServiceChoice ::= ENUMERATED */
typedef enum BACnet_Unconfirmed_Service_Choice {
SERVICE_UNCONFIRMED_I_AM = 0, SERVICE_UNCONFIRMED_I_AM = 0,
SERVICE_UNCONFIRMED_I_HAVE = 1, SERVICE_UNCONFIRMED_I_HAVE = 1,
SERVICE_UNCONFIRMED_COV_NOTIFICATION = 2, SERVICE_UNCONFIRMED_COV_NOTIFICATION = 2,
@@ -1475,16 +1487,21 @@ typedef enum {
SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9, SERVICE_UNCONFIRMED_UTC_TIME_SYNCHRONIZATION = 9,
/* addendum 2010-aa */ /* addendum 2010-aa */
SERVICE_UNCONFIRMED_WRITE_GROUP = 10, SERVICE_UNCONFIRMED_WRITE_GROUP = 10,
/* addendum 2012-aq */
SERVICE_UNCONFIRMED_COV_NOTIFICATION_MULTIPLE = 11,
/* addendum 2016-bz */
SERVICE_UNCONFIRMED_WHO_AM_I = 13,
SERVICE_UNCONFIRMED_YOU_ARE = 14,
/* Other services to be added as they are defined. */ /* Other services to be added as they are defined. */
/* All choice values in this production are reserved */ /* All choice values in this production are reserved */
/* for definition by ASHRAE. */ /* for definition by ASHRAE. */
/* Proprietary extensions are made by using the */ /* Proprietary extensions are made by using the */
/* UnconfirmedPrivateTransfer service. See Clause 23. */ /* UnconfirmedPrivateTransfer service. See Clause 23. */
MAX_BACNET_UNCONFIRMED_SERVICE = 11 MAX_BACNET_UNCONFIRMED_SERVICE = 15
} BACNET_UNCONFIRMED_SERVICE; } BACNET_UNCONFIRMED_SERVICE;
/* Bit String Enumerations */ /* BACnetServicesSupported ::= BIT STRING */
typedef enum { typedef enum BACnet_Services_Supported {
/* Alarm and Event Services */ /* Alarm and Event Services */
SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM = 0, SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM = 0,
SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION = 1, SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION = 1,
@@ -1494,7 +1511,10 @@ typedef enum {
SERVICE_SUPPORTED_GET_EVENT_INFORMATION = 39, SERVICE_SUPPORTED_GET_EVENT_INFORMATION = 39,
SERVICE_SUPPORTED_SUBSCRIBE_COV = 5, SERVICE_SUPPORTED_SUBSCRIBE_COV = 5,
SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY = 38, SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY = 38,
SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY_MULTIPLE = 41,
SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION = 37, SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION = 37,
SERVICE_SUPPORTED_CONFIRMED_AUDIT_NOTIFICATION = 44,
SERVICE_SUPPORTED_AUDIT_LOG_QUERY = 45,
/* File Access Services */ /* File Access Services */
SERVICE_SUPPORTED_ATOMIC_READ_FILE = 6, SERVICE_SUPPORTED_ATOMIC_READ_FILE = 6,
SERVICE_SUPPORTED_ATOMIC_WRITE_FILE = 7, SERVICE_SUPPORTED_ATOMIC_WRITE_FILE = 7,
@@ -1515,6 +1535,8 @@ typedef enum {
SERVICE_SUPPORTED_PRIVATE_TRANSFER = 18, SERVICE_SUPPORTED_PRIVATE_TRANSFER = 18,
SERVICE_SUPPORTED_TEXT_MESSAGE = 19, SERVICE_SUPPORTED_TEXT_MESSAGE = 19,
SERVICE_SUPPORTED_REINITIALIZE_DEVICE = 20, SERVICE_SUPPORTED_REINITIALIZE_DEVICE = 20,
SERVICE_SUPPORTED_WHO_AM_I = 47,
SERVICE_SUPPORTED_YOU_ARE = 48,
/* Virtual Terminal Services */ /* Virtual Terminal Services */
SERVICE_SUPPORTED_VT_OPEN = 21, SERVICE_SUPPORTED_VT_OPEN = 21,
SERVICE_SUPPORTED_VT_CLOSE = 22, SERVICE_SUPPORTED_VT_CLOSE = 22,
@@ -1524,17 +1546,21 @@ typedef enum {
SERVICE_SUPPORTED_REQUEST_KEY = 25, SERVICE_SUPPORTED_REQUEST_KEY = 25,
SERVICE_SUPPORTED_I_AM = 26, SERVICE_SUPPORTED_I_AM = 26,
SERVICE_SUPPORTED_I_HAVE = 27, SERVICE_SUPPORTED_I_HAVE = 27,
/* Unconfirmed Services */
SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION = 28, SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION = 28,
SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION_MULTIPLE = 43,
SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION = 29, SERVICE_SUPPORTED_UNCONFIRMED_EVENT_NOTIFICATION = 29,
SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER = 30, SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER = 30,
SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE = 31, SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE = 31,
SERVICE_SUPPORTED_TIME_SYNCHRONIZATION = 32, SERVICE_SUPPORTED_TIME_SYNCHRONIZATION = 32,
SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION = 36, SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION = 36,
SERVICE_SUPPORTED_WHO_HAS = 33, SERVICE_SUPPORTED_WHO_HAS = 33,
SERVICE_SUPPORTED_WHO_IS = 34 SERVICE_SUPPORTED_WHO_IS = 34,
SERVICE_SUPPORTED_UNCONFIRMED_AUDIT_NOTIFICATION = 46,
/* Other services to be added as they are defined. */ /* Other services to be added as they are defined. */
/* All values in this production are reserved */ /* All values in this production are reserved */
/* for definition by ASHRAE. */ /* for definition by ASHRAE. */
BACNET_SERVICES_SUPPORTED_MAX = 47
} BACNET_SERVICES_SUPPORTED; } BACNET_SERVICES_SUPPORTED;
/* Bit String Enumerations */ /* Bit String Enumerations */
+64 -64
View File
@@ -39,6 +39,7 @@
#include "bacnet/bacdef.h" #include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h" #include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h" #include "bacnet/bacenum.h"
#include "bacnet/bacerror.h"
#include "bacnet/dcc.h" #include "bacnet/dcc.h"
#include "bacnet/iam.h" #include "bacnet/iam.h"
/* basic objects, services, TSM */ /* basic objects, services, TSM */
@@ -302,7 +303,36 @@ void apdu_set_confirmed_ack_handler(
} }
} }
static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE]; static union {
error_function error;
complex_error_function complex;
} Error_Function[MAX_BACNET_CONFIRMED_SERVICE];
/**
* @brief Determine if the service uses Complex Error function
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
* @return true if the service uses a Complex Error function
*/
bool apdu_complex_error(BACNET_CONFIRMED_SERVICE service_choice)
{
bool status = false;
switch (service_choice) {
case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY_MULTIPLE:
case SERVICE_CONFIRMED_ADD_LIST_ELEMENT:
case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT:
case SERVICE_CONFIRMED_CREATE_OBJECT:
case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE:
case SERVICE_CONFIRMED_PRIVATE_TRANSFER:
case SERVICE_CONFIRMED_VT_CLOSE:
status = true;
break;
default:
break;
}
return status;
}
/** /**
* @brief Set a error handler function for the given confirmed service. * @brief Set a error handler function for the given confirmed service.
@@ -312,10 +342,28 @@ static error_function Error_Function[MAX_BACNET_CONFIRMED_SERVICE];
* handling. * handling.
*/ */
void apdu_set_error_handler( void apdu_set_error_handler(
BACNET_CONFIRMED_SERVICE service_choice, error_function pFunction) BACNET_CONFIRMED_SERVICE service_choice,
error_function pFunction)
{ {
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { if ((service_choice < MAX_BACNET_CONFIRMED_SERVICE) &&
Error_Function[service_choice] = pFunction; (!apdu_complex_error(service_choice))) {
Error_Function[service_choice].error = pFunction;
}
}
/**
* @brief Set a complex error handler function for the given confirmed service.
*
* @param service_choice Service, see SERVICE_CONFIRMED_X enumeration.
* @param pFunction Pointer to the function, being in charge of the error
* handling.
*/
void apdu_set_complex_error_handler(
BACNET_CONFIRMED_SERVICE service_choice,
complex_error_function pFunction)
{
if (apdu_complex_error(service_choice)) {
Error_Function[service_choice].complex = pFunction;
} }
} }
@@ -499,8 +547,6 @@ void apdu_handler(BACNET_ADDRESS *src,
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 */
uint8_t tag_number = 0;
uint32_t len_value = 0;
uint32_t error_code = 0; uint32_t error_code = 0;
uint32_t error_class = 0; uint32_t error_class = 0;
uint8_t reason = 0; uint8_t reason = 0;
@@ -648,65 +694,19 @@ void apdu_handler(BACNET_ADDRESS *src,
if (apdu_len >= 3) { if (apdu_len >= 3) {
invoke_id = apdu[1]; invoke_id = apdu[1];
service_choice = apdu[2]; service_choice = apdu[2];
len = 3; if (apdu_complex_error(service_choice)) {
if (Error_Function[service_choice].complex) {
/* FIXME: Currently special case for C_P_T but there are Error_Function[service_choice].complex(src,
others which may need consideration such as invoke_id, service_choice,
ChangeList-Error, CreateObject-Error, &apdu[3], apdu_len - 3);
WritePropertyMultiple-Error and VTClose_Error but they
may be left as is for now until support for these
services is added */
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) { /* skip over
opening tag 0
*/
if (decode_is_opening_tag_number(&apdu[len], 0)) {
len++; /* a tag number of 0 is not extended so only
one octet */
} }
} } else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
len = bacerror_decode_error_class_and_code(&apdu[3],
if (len < apdu_len) { apdu_len - 3, &error_class, &error_code);
len += decode_tag_number_and_value( if ((len != 0) &&
&apdu[len], &tag_number, &len_value); (Error_Function[service_choice].error)) {
Error_Function[service_choice].error(src,
if (len < apdu_len) { invoke_id,
/* FIXME: we could validate that the tag is
* enumerated... */
len += decode_enumerated(
&apdu[len], len_value, &error_class);
if (len < apdu_len) {
len += decode_tag_number_and_value(
&apdu[len], &tag_number, &len_value);
if (len < apdu_len) {
/* FIXME: we could validate that the tag is
* enumerated... */
len += decode_enumerated(
&apdu[len], len_value, &error_code);
if (service_choice ==
SERVICE_CONFIRMED_PRIVATE_TRANSFER) {
if (len < apdu_len) {
/* skip over closing tag 0 */
if (decode_is_closing_tag_number(
&apdu[len], 0)) {
len++; /* a tag number of 0 is
not extended so only
one octet */
}
}
}
}
}
}
}
if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) {
if (Error_Function[service_choice]) {
Error_Function[service_choice](src, invoke_id,
(BACNET_ERROR_CLASS)error_class, (BACNET_ERROR_CLASS)error_class,
(BACNET_ERROR_CODE)error_code); (BACNET_ERROR_CODE)error_code);
} }
+17
View File
@@ -96,6 +96,15 @@ extern "C" {
BACNET_ERROR_CLASS error_class, BACNET_ERROR_CLASS error_class,
BACNET_ERROR_CODE error_code); BACNET_ERROR_CODE error_code);
/* complex error reply function */
typedef void (
*complex_error_function) (
BACNET_ADDRESS * src,
uint8_t invoke_id,
uint8_t service_choice,
uint8_t * service_request,
uint16_t service_len);
/* generic abort reply function */ /* generic abort reply function */
typedef void ( typedef void (
*abort_function) ( *abort_function) (
@@ -150,12 +159,20 @@ extern "C" {
size_t * index, size_t * index,
bool * bIsConfirmed); bool * bIsConfirmed);
BACNET_STACK_EXPORT
bool apdu_complex_error(
BACNET_CONFIRMED_SERVICE service_choice);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void apdu_set_error_handler( void apdu_set_error_handler(
BACNET_CONFIRMED_SERVICE service_choice, BACNET_CONFIRMED_SERVICE service_choice,
error_function pFunction); error_function pFunction);
BACNET_STACK_EXPORT
void apdu_set_complex_error_handler(
BACNET_CONFIRMED_SERVICE service_choice,
complex_error_function pFunction);
BACNET_STACK_EXPORT BACNET_STACK_EXPORT
void apdu_set_abort_handler( void apdu_set_abort_handler(
abort_function pFunction); abort_function pFunction);
+136
View File
@@ -29,6 +29,7 @@
#include "bacnet/bacenum.h" #include "bacnet/bacenum.h"
#include "bacnet/bacdcode.h" #include "bacnet/bacdcode.h"
#include "bacnet/bacdef.h" #include "bacnet/bacdef.h"
#include "bacnet/bacerror.h"
#include "bacnet/wp.h" #include "bacnet/wp.h"
#include "bacnet/wpm.h" #include "bacnet/wpm.h"
@@ -435,3 +436,138 @@ int wpm_error_ack_encode_apdu(
} }
return len; return len;
} }
/** Decoding for WritePropertyMultiple Error
* @ingroup DSWPM
* WritePropertyMultiple-Error ::= SEQUENCE {
* error-type [0] Error,
* first-failed-write-attempt [1] BACnetObjectPropertyReference
* }
*
* @param apdu [in] The contents of the APDU buffer.
* @param apdu_size [in] The size of the APDU buffer.
* @param wp_data [out] The BACNET_WRITE_PROPERTY_DATA structure
* which will contain the response values or error.
*
* @return Count of decoded bytes.
*/
int wpm_error_ack_decode_apdu(
uint8_t *apdu, uint16_t apdu_size, BACNET_WRITE_PROPERTY_DATA *wp_data)
{
int len = 0, apdu_len = 0;
uint8_t *apdu_offset = NULL;
BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES;
BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS;
BACNET_OBJECT_PROPERTY_REFERENCE value;
if (apdu) {
apdu_offset = apdu;
}
if (wp_data) {
wp_data->error_class = ERROR_CLASS_SERVICES;
wp_data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
}
/* Context tag 0 - Error */
if (apdu_size == 0) {
return 0;
}
if (decode_is_opening_tag_number(apdu_offset, 0)) {
len = 1;
apdu_len -= len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
if (apdu_size > len) {
apdu_size -= len;
} else {
return 0;
}
}
} else {
return 0;
}
len = bacerror_decode_error_class_and_code(apdu_offset, apdu_size,
&error_class, &error_code);
if (len > 0) {
if (wp_data) {
wp_data->error_class = error_class;
wp_data->error_code = error_code;
}
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
if (apdu_size > len) {
apdu_size -= len;
} else {
return 0;
}
}
} else {
return 0;
}
if (apdu_size == 0) {
return 0;
}
if (decode_is_closing_tag_number(apdu_offset, 0)) {
len = 1;
apdu_len -= len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
if (apdu_size > len) {
apdu_size -= len;
} else {
return 0;
}
}
} else {
return 0;
}
/* Context tag 1 - BACnetObjectPropertyReference */
if (apdu_size == 0) {
return 0;
}
if (decode_is_opening_tag_number(apdu_offset, 1)) {
len = 1;
apdu_len -= len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
if (apdu_size > len) {
apdu_size -= len;
} else {
return 0;
}
}
} else {
return 0;
}
len = bacapp_decode_obj_property_ref(apdu_offset, apdu_size, &value);
if (len > 0) {
if (wp_data) {
wp_data->object_instance = value.object_identifier.instance;
wp_data->object_type = value.object_identifier.type;
wp_data->object_property = value.property_identifier;
wp_data->array_index = value.property_array_index;
}
apdu_len += len;
if (apdu) {
apdu_offset = &apdu[apdu_len];
if (apdu_size > len) {
apdu_size -= len;
} else {
return 0;
}
}
} else {
return 0;
}
if (apdu_size == 0) {
return 0;
}
if (decode_is_closing_tag_number(apdu_offset, 1)) {
len = 1;
apdu_len += len;
} else {
return 0;
}
return apdu_len;
}
+5
View File
@@ -93,6 +93,11 @@ extern "C" {
uint8_t invoke_id, uint8_t invoke_id,
BACNET_WRITE_PROPERTY_DATA * wp_data); BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_STACK_EXPORT
int wpm_error_ack_decode_apdu(
uint8_t *apdu,
uint16_t apdu_size,
BACNET_WRITE_PROPERTY_DATA * wp_data);
#ifdef __cplusplus #ifdef __cplusplus
} }
+50 -1
View File
@@ -114,6 +114,54 @@ static void testDevIdRef(void)
zassert_equal( zassert_equal(
inData.deviceIdentifier.type, outData.deviceIdentifier.type, NULL); inData.deviceIdentifier.type, outData.deviceIdentifier.type, NULL);
} }
static void testObjPropRef(void)
{
BACNET_OBJECT_PROPERTY_REFERENCE inData;
BACNET_OBJECT_PROPERTY_REFERENCE outData;
uint8_t apdu[MAX_APDU];
uint8_t tag_number = 1;
int inLen;
int outLen;
inData.object_identifier.instance = 12345;
inData.object_identifier.type = OBJECT_ANALOG_VALUE;
inData.property_identifier = PROP_PRESENT_VALUE;
inData.property_array_index = BACNET_ARRAY_ALL;
inLen = bacapp_encode_obj_property_ref(apdu, &inData);
outLen = bacapp_decode_obj_property_ref(apdu, inLen, &outData);
zassert_equal(outLen, inLen, NULL);
zassert_equal(
inData.object_identifier.type,
outData.object_identifier.type, NULL);
zassert_equal(
inData.object_identifier.instance,
outData.object_identifier.instance, NULL);
zassert_equal(
inData.property_identifier,
outData.property_identifier, NULL);
zassert_equal(
inData.property_array_index,
outData.property_array_index, NULL);
/* context */
inLen = bacapp_encode_context_obj_property_ref(apdu, tag_number, &inData);
outLen = bacapp_decode_context_obj_property_ref(apdu, inLen, tag_number,
&outData);
zassert_equal(outLen, inLen, NULL);
zassert_equal(
inData.object_identifier.type,
outData.object_identifier.type, NULL);
zassert_equal(
inData.object_identifier.instance,
outData.object_identifier.instance, NULL);
zassert_equal(
inData.property_identifier,
outData.property_identifier, NULL);
zassert_equal(
inData.property_array_index,
outData.property_array_index, NULL);
}
/** /**
* @} * @}
*/ */
@@ -123,7 +171,8 @@ void test_main(void)
{ {
ztest_test_suite(bacdevobjpropref_tests, ztest_test_suite(bacdevobjpropref_tests,
ztest_unit_test(testDevIdPropRef), ztest_unit_test(testDevIdPropRef),
ztest_unit_test(testDevIdRef) ztest_unit_test(testDevIdRef),
ztest_unit_test(testObjPropRef)
); );
ztest_run_test_suite(bacdevobjpropref_tests); ztest_run_test_suite(bacdevobjpropref_tests);
+1
View File
@@ -36,6 +36,7 @@ add_executable(${PROJECT_NAME}
# Support files and stubs (pathname alphabetical) # Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaddr.c ${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c ${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacerror.c
${SRC_DIR}/bacnet/bacint.c ${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c ${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c ${SRC_DIR}/bacnet/bacstr.c