Feature/add create object service (#476)
Added BACnet CreateObject and DeleteObject services * refactored codec for BACnetPropertyValue into bacapp module * added unit tests for BACnetPropertyValue * refactored COV and Events to use BACnetPropertyValue codec API * added unit tests for COV * added overrun safe decoders for tag numbers and boolean context * added unit tests and codecs for CreateObject and DeleteObject services * added APDU service handers and senders for CreateObject and DeleteObject services * added command line apps bacco and bacdo for CreateObject and DeleteObject services * added CreateObject and DeleteObject service handling in example server app and device object * added new BACnetRejectReason, Error Class, and BACnetAbortReason enumerations and conversions --------- Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
+64
-2
@@ -73,6 +73,21 @@ BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
case ERROR_CODE_ABORT_INSUFFICIENT_SECURITY:
|
||||
abort_code = ABORT_REASON_INSUFFICIENT_SECURITY;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE:
|
||||
abort_code = ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME:
|
||||
abort_code = ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_OUT_OF_RESOURCES:
|
||||
abort_code = ABORT_REASON_OUT_OF_RESOURCES;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_TSM_TIMEOUT:
|
||||
abort_code = ABORT_REASON_TSM_TIMEOUT;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_APDU_TOO_LONG:
|
||||
abort_code = ABORT_REASON_APDU_TOO_LONG;
|
||||
break;
|
||||
case ERROR_CODE_ABORT_PROPRIETARY:
|
||||
abort_code = ABORT_REASON_PROPRIETARY_FIRST;
|
||||
break;
|
||||
@@ -85,6 +100,38 @@ BACNET_ABORT_REASON abort_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
return (abort_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if a BACnetErrorCode is a BACnetAbortReason
|
||||
* @param error_code #BACNET_ERROR_CODE enumeration
|
||||
* @return true if the BACnet Error Code is a BACnet abort reason
|
||||
*/
|
||||
bool abort_valid_error_code(BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
switch (error_code) {
|
||||
case ERROR_CODE_ABORT_OTHER:
|
||||
case ERROR_CODE_ABORT_BUFFER_OVERFLOW:
|
||||
case ERROR_CODE_ABORT_INVALID_APDU_IN_THIS_STATE:
|
||||
case ERROR_CODE_ABORT_PREEMPTED_BY_HIGHER_PRIORITY_TASK:
|
||||
case ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED:
|
||||
case ERROR_CODE_ABORT_SECURITY_ERROR:
|
||||
case ERROR_CODE_ABORT_INSUFFICIENT_SECURITY:
|
||||
case ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE:
|
||||
case ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME:
|
||||
case ERROR_CODE_ABORT_OUT_OF_RESOURCES:
|
||||
case ERROR_CODE_ABORT_TSM_TIMEOUT:
|
||||
case ERROR_CODE_ABORT_APDU_TOO_LONG:
|
||||
case ERROR_CODE_ABORT_PROPRIETARY:
|
||||
status = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert error-code from abort-reason
|
||||
*
|
||||
@@ -101,6 +148,9 @@ BACNET_ERROR_CODE abort_convert_to_error_code(BACNET_ABORT_REASON abort_code)
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_ABORT_OTHER;
|
||||
|
||||
switch (abort_code) {
|
||||
case ABORT_REASON_OTHER:
|
||||
error_code = ERROR_CODE_ABORT_OTHER;
|
||||
break;
|
||||
case ABORT_REASON_BUFFER_OVERFLOW:
|
||||
error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW;
|
||||
break;
|
||||
@@ -119,8 +169,20 @@ BACNET_ERROR_CODE abort_convert_to_error_code(BACNET_ABORT_REASON abort_code)
|
||||
case ABORT_REASON_INSUFFICIENT_SECURITY:
|
||||
error_code = ERROR_CODE_ABORT_INSUFFICIENT_SECURITY;
|
||||
break;
|
||||
case ABORT_REASON_OTHER:
|
||||
error_code = ERROR_CODE_ABORT_OTHER;
|
||||
case ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE:
|
||||
error_code = ERROR_CODE_ABORT_WINDOW_SIZE_OUT_OF_RANGE;
|
||||
break;
|
||||
case ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME:
|
||||
error_code = ERROR_CODE_ABORT_APPLICATION_EXCEEDED_REPLY_TIME;
|
||||
break;
|
||||
case ABORT_REASON_OUT_OF_RESOURCES:
|
||||
error_code = ERROR_CODE_ABORT_OUT_OF_RESOURCES;
|
||||
break;
|
||||
case ABORT_REASON_TSM_TIMEOUT:
|
||||
error_code = ERROR_CODE_ABORT_TSM_TIMEOUT;
|
||||
break;
|
||||
case ABORT_REASON_APDU_TOO_LONG:
|
||||
error_code = ERROR_CODE_ABORT_APDU_TOO_LONG;
|
||||
break;
|
||||
default:
|
||||
if ((abort_code >= ABORT_REASON_PROPRIETARY_FIRST) &&
|
||||
|
||||
+5
-2
@@ -21,8 +21,8 @@
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef ABORT_H
|
||||
#define ABORT_H
|
||||
#ifndef BACNET_ABORT_H
|
||||
#define BACNET_ABORT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -37,6 +37,9 @@ extern "C" {
|
||||
BACNET_ABORT_REASON abort_convert_error_code(
|
||||
BACNET_ERROR_CODE error_code);
|
||||
BACNET_STACK_EXPORT
|
||||
bool abort_valid_error_code(
|
||||
BACNET_ERROR_CODE error_code);
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_ERROR_CODE abort_convert_to_error_code(
|
||||
BACNET_ABORT_REASON abort_code);
|
||||
|
||||
|
||||
+258
-29
@@ -39,7 +39,7 @@
|
||||
#include <ctype.h> /* for isalnum */
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
#endif
|
||||
@@ -198,8 +198,8 @@ int bacapp_encode_application_data(
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
/* BACnetDestination */
|
||||
apdu_len = bacnet_destination_encode(
|
||||
apdu, &value->type.Destination);
|
||||
apdu_len =
|
||||
bacnet_destination_encode(apdu, &value->type.Destination);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -351,8 +351,8 @@ int bacapp_decode_data(uint8_t *apdu,
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
/* BACnetDestination */
|
||||
len = bacnet_destination_decode(apdu, len_value_type,
|
||||
&value->type.Destination);
|
||||
len = bacnet_destination_decode(
|
||||
apdu, len_value_type, &value->type.Destination);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -684,8 +684,8 @@ int bacapp_encode_context_data_value(uint8_t *apdu,
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
/* BACnetDestination */
|
||||
apdu_len = bacnet_destination_context_encode(apdu,
|
||||
context_tag_number, &value->type.Destination);
|
||||
apdu_len = bacnet_destination_context_encode(
|
||||
apdu, context_tag_number, &value->type.Destination);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -1535,8 +1535,8 @@ int bacapp_data_len(
|
||||
}
|
||||
do {
|
||||
if (bacnet_is_opening_tag(apdu, apdu_len_max)) {
|
||||
len = bacnet_tag_number_and_value_decode(apdu,
|
||||
apdu_len_max - apdu_len, &tag_number, &value);
|
||||
len = bacnet_tag_number_and_value_decode(
|
||||
apdu, apdu_len_max - apdu_len, &tag_number, &value);
|
||||
if (opening_tag_number_counter == 0) {
|
||||
opening_tag_number = tag_number;
|
||||
opening_tag_number_counter = 1;
|
||||
@@ -1546,8 +1546,8 @@ int bacapp_data_len(
|
||||
opening_tag_number_counter++;
|
||||
}
|
||||
} else if (bacnet_is_closing_tag(apdu, apdu_len_max)) {
|
||||
len = bacnet_tag_number_and_value_decode(apdu,
|
||||
apdu_len_max - apdu_len, &tag_number, &value);
|
||||
len = bacnet_tag_number_and_value_decode(
|
||||
apdu, apdu_len_max - apdu_len, &tag_number, &value);
|
||||
if (tag_number == opening_tag_number) {
|
||||
if (opening_tag_number_counter > 0) {
|
||||
opening_tag_number_counter--;
|
||||
@@ -1585,7 +1585,6 @@ int bacapp_data_len(
|
||||
}
|
||||
} while (opening_tag_number_counter > 0);
|
||||
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
@@ -1603,13 +1602,14 @@ static int bacapp_snprintf_date(char *str, size_t str_len, BACNET_DATE *bdate)
|
||||
{
|
||||
int ret_val = 0;
|
||||
int slen = 0;
|
||||
const char *weekday_text, *month_text;
|
||||
|
||||
weekday_text = bactext_day_of_week_name(bdate->wday);
|
||||
month_text = bactext_month_name(bdate->month);
|
||||
/* false positive cppcheck - snprintf allows null pointers */
|
||||
/* cppcheck-suppress nullPointer */
|
||||
/* cppcheck-suppress ctunullpointer */
|
||||
slen = snprintf(str, str_len, "%s, %s",
|
||||
bactext_day_of_week_name(bdate->wday),
|
||||
bactext_month_name(bdate->month));
|
||||
slen = snprintf(str, str_len, "%s, %s", weekday_text, month_text);
|
||||
if (str) {
|
||||
str += slen;
|
||||
if (str_len >= slen) {
|
||||
@@ -1873,7 +1873,7 @@ int bacapp_snprintf_value(
|
||||
#if defined(BACAPP_OCTET_STRING) || defined(BACAPP_TYPES_EXTRA)
|
||||
uint8_t *octet_str;
|
||||
#endif
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
|
||||
/* Wide character (decoded from multi-byte character). */
|
||||
wchar_t wc;
|
||||
/* Wide character length in bytes. */
|
||||
@@ -1953,7 +1953,7 @@ int bacapp_snprintf_value(
|
||||
}
|
||||
}
|
||||
ret_val += slen;
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined (__STDC_ISO_10646__)
|
||||
#if (__STDC_VERSION__ >= 199901L) && defined(__STDC_ISO_10646__)
|
||||
if (characterstring_encoding(&value->type.Character_String) ==
|
||||
CHARACTER_UTF8) {
|
||||
while (len > 0) {
|
||||
@@ -2201,14 +2201,16 @@ int bacapp_snprintf_value(
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_TIMESTAMP:
|
||||
/*ISO 8601 format */
|
||||
slen = snprintf(str, str_len, "%04u-%02u-%02uT%02u:%02u:%02u.%03u",
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.date.year,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.date.month,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.date.day,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.time.hour,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.time.min,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.time.sec,
|
||||
(unsigned) value->type.Time_Stamp.value.dateTime.time.hundredths);
|
||||
slen = snprintf(str, str_len,
|
||||
"%04u-%02u-%02uT%02u:%02u:%02u.%03u",
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.date.year,
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.date.month,
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.date.day,
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.time.hour,
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.time.min,
|
||||
(unsigned)value->type.Time_Stamp.value.dateTime.time.sec,
|
||||
(unsigned)
|
||||
value->type.Time_Stamp.value.dateTime.time.hundredths);
|
||||
ret_val += slen;
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_LIGHTING_COMMAND:
|
||||
@@ -2279,7 +2281,8 @@ int bacapp_snprintf_value(
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
/* BACnetWeeklySchedule */
|
||||
ret_val = bacnet_destination_to_ascii(&value->type.Destination, str, str_len);
|
||||
ret_val = bacnet_destination_to_ascii(
|
||||
&value->type.Destination, str, str_len);
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_HOST_N_PORT:
|
||||
if (value->type.Host_Address.host_ip_address) {
|
||||
@@ -2803,8 +2806,8 @@ bool bacapp_parse_application_data(BACNET_APPLICATION_TAG tag_number,
|
||||
}
|
||||
break;
|
||||
case BACNET_APPLICATION_TAG_DESTINATION:
|
||||
status = bacnet_destination_from_ascii(&value->type.Destination,
|
||||
argv);
|
||||
status = bacnet_destination_from_ascii(
|
||||
&value->type.Destination, argv);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
@@ -2868,6 +2871,231 @@ void bacapp_property_value_list_init(BACNET_PROPERTY_VALUE *value, size_t count)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Link an array of BACNET_PROPERTY_VALUE elements.
|
||||
* The linked-list is used prior to encoding or decoding
|
||||
* the APDU data into the structure.
|
||||
*
|
||||
* @param value_list - Pointer to the first BACNET_PROPERTY_VALUE element in
|
||||
* an array
|
||||
* @param count - number of BACNET_PROPERTY_VALUE elements to link
|
||||
*/
|
||||
void bacapp_property_value_list_link(
|
||||
BACNET_PROPERTY_VALUE *value_list, size_t count)
|
||||
{
|
||||
BACNET_PROPERTY_VALUE *current_value_list = NULL;
|
||||
|
||||
if (value_list) {
|
||||
while (count) {
|
||||
if (count > 1) {
|
||||
current_value_list = value_list;
|
||||
value_list++;
|
||||
current_value_list->next = value_list;
|
||||
} else {
|
||||
value_list->next = NULL;
|
||||
}
|
||||
count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode one BACnetPropertyValue value
|
||||
*
|
||||
* BACnetPropertyValue ::= SEQUENCE {
|
||||
* property-identifier [0] BACnetPropertyIdentifier,
|
||||
* property-array-index [1] Unsigned OPTIONAL,
|
||||
* -- used only with array datatypes
|
||||
* -- if omitted with an array the entire array is referenced
|
||||
* property-value [2] ABSTRACT-SYNTAX.&Type,
|
||||
* -- any datatype appropriate for the specified property
|
||||
* priority [3] Unsigned (1..16) OPTIONAL
|
||||
* -- used only when property is commandable
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoded values, or NULL for length
|
||||
* @param value Pointer to the service data used for encoding values
|
||||
*
|
||||
* @return Bytes encoded or zero on error.
|
||||
*/
|
||||
int bacapp_property_value_encode(uint8_t *apdu, BACNET_PROPERTY_VALUE *value)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
|
||||
|
||||
if (value) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
len = encode_context_enumerated(apdu, 0, value->propertyIdentifier);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 1 - propertyArrayIndex OPTIONAL */
|
||||
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
|
||||
len = encode_context_unsigned(apdu, 1, value->propertyArrayIndex);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
}
|
||||
/* tag 2 - value */
|
||||
/* abstract syntax gets enclosed in a context tag */
|
||||
len = encode_opening_tag(apdu, 2);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
app_data = &value->value;
|
||||
while (app_data != NULL) {
|
||||
len = bacapp_encode_application_data(apdu, app_data);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
app_data = app_data->next;
|
||||
}
|
||||
len = encode_closing_tag(apdu, 2);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 3 - priority OPTIONAL */
|
||||
if (value->priority != BACNET_NO_PRIORITY) {
|
||||
len = encode_context_unsigned(apdu, 3, value->priority);
|
||||
apdu_len += len;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode one BACnetPropertyValue value
|
||||
*
|
||||
* BACnetPropertyValue ::= SEQUENCE {
|
||||
* property-identifier [0] BACnetPropertyIdentifier,
|
||||
* property-array-index [1] Unsigned OPTIONAL,
|
||||
* -- used only with array datatypes
|
||||
* -- if omitted with an array the entire array is referenced
|
||||
* property-value [2] ABSTRACT-SYNTAX.&Type,
|
||||
* -- any datatype appropriate for the specified property
|
||||
* priority [3] Unsigned (1..16) OPTIONAL
|
||||
* -- used only when property is commandable
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer of encoded value
|
||||
* @param apdu_size Size of the buffer holding the encode value
|
||||
* @param value Pointer to the service data used for encoding values
|
||||
*
|
||||
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int bacapp_property_value_decode(
|
||||
uint8_t *apdu, uint32_t apdu_size, BACNET_PROPERTY_VALUE *value)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
int tag_len = 0;
|
||||
uint32_t enumerated_value = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
|
||||
BACNET_PROPERTY_ID property_identifier = PROP_ALL;
|
||||
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
|
||||
|
||||
/* property-identifier [0] BACnetPropertyIdentifier */
|
||||
len = bacnet_enumerated_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
property_identifier = enumerated_value;
|
||||
if (value) {
|
||||
value->propertyIdentifier = property_identifier;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* property-array-index [1] Unsigned OPTIONAL */
|
||||
if (bacnet_is_context_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 1, NULL)) {
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else if (unsigned_value > UINT32_MAX) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
if (value) {
|
||||
value->propertyArrayIndex = unsigned_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
value->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
}
|
||||
/* property-value [2] ABSTRACT-SYNTAX.&Type */
|
||||
if (bacnet_is_opening_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
|
||||
if (value) {
|
||||
apdu_len += len;
|
||||
app_data = &value->value;
|
||||
while (app_data != NULL) {
|
||||
len = bacapp_decode_application_data(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, app_data);
|
||||
if (len < 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
|
||||
break;
|
||||
}
|
||||
app_data = app_data->next;
|
||||
}
|
||||
} else {
|
||||
/* this len function needs to start at the opening tag
|
||||
to match opening/closing tags like a stack.
|
||||
However, it returns the len between the tags.
|
||||
Therefore, store the length of the opening tag first */
|
||||
tag_len = len;
|
||||
len = bacapp_data_len(&apdu[apdu_len], apdu_size - apdu_len,
|
||||
(BACNET_PROPERTY_ID)property_identifier);
|
||||
apdu_len += len;
|
||||
/* add the opening tag length to the totals */
|
||||
apdu_len += tag_len;
|
||||
}
|
||||
if (bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 2, &len)) {
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* priority [3] Unsigned (1..16) OPTIONAL */
|
||||
if (bacnet_is_context_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 3, NULL)) {
|
||||
len = bacnet_unsigned_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 3, &unsigned_value);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else if (unsigned_value > UINT8_MAX) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
apdu_len += len;
|
||||
if (value) {
|
||||
value->priority = unsigned_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
value->priority = BACNET_NO_PRIORITY;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/* generic - can be used by other unit tests
|
||||
returns true if matching or same, false if different */
|
||||
bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value,
|
||||
@@ -2922,7 +3150,8 @@ bool bacapp_same_value(BACNET_APPLICATION_DATA_VALUE *value,
|
||||
#endif
|
||||
#if defined(BACAPP_DOUBLE)
|
||||
case BACNET_APPLICATION_TAG_DOUBLE:
|
||||
if (!islessgreater(test_value->type.Double,value->type.Double)) {
|
||||
if (!islessgreater(
|
||||
test_value->type.Double, value->type.Double)) {
|
||||
status = true;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -155,6 +155,21 @@ extern "C" {
|
||||
BACNET_PROPERTY_VALUE *value,
|
||||
size_t count);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void bacapp_property_value_list_link(
|
||||
BACNET_PROPERTY_VALUE *value_list,
|
||||
size_t count);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_property_value_encode(
|
||||
uint8_t *apdu,
|
||||
BACNET_PROPERTY_VALUE *value);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_property_value_decode(
|
||||
uint8_t *apdu,
|
||||
uint32_t apdu_size,
|
||||
BACNET_PROPERTY_VALUE *value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacapp_encode_data(
|
||||
uint8_t * apdu,
|
||||
|
||||
@@ -696,6 +696,39 @@ bool decode_is_context_tag_with_length(
|
||||
return (bool)(IS_CONTEXT_SPECIFIC(*apdu) && (my_tag_number == tag_number));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the tag is context specific
|
||||
* and matches, as defined in clause 20.2.1.3.2 Constructed
|
||||
* Data. This function returns the tag length as well.
|
||||
*
|
||||
* @param apdu Pointer to the tag begin.
|
||||
* @param apdu_size - number of bytes in the buffer
|
||||
* @param tag_number Tag number, that has been decoded before.
|
||||
* @param tag_length Pointer to a variable, or NULL.
|
||||
* Returns the length of the tag in bytes if not NULL.
|
||||
*
|
||||
* @return true on a match, false otherwise.
|
||||
*/
|
||||
bool bacnet_is_context_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
|
||||
{
|
||||
bool match = false;
|
||||
uint8_t my_tag_number = 0;
|
||||
int len;
|
||||
|
||||
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
|
||||
if ((len > 0) && (my_tag_number == tag_number)) {
|
||||
if (IS_CONTEXT_SPECIFIC(apdu[0])) {
|
||||
if (tag_length) {
|
||||
*tag_length = len;
|
||||
}
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the tag does match and it
|
||||
* is an opening tag as well.
|
||||
@@ -715,6 +748,39 @@ bool decode_is_opening_tag_number(uint8_t *apdu, uint8_t tag_number)
|
||||
return (bool)(IS_OPENING_TAG(apdu[0]) && (my_tag_number == tag_number));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the tag does match and it
|
||||
* is an opening tag as well.
|
||||
* As defined in clause 20.2.1.3.2 Constructed Data.
|
||||
*
|
||||
* @param apdu Pointer to the tag begin.
|
||||
* @param apdu_size - number of bytes in the buffer
|
||||
* @param tag_number Tag number, that has been decoded before.
|
||||
* @param tag_length Pointer to a variable, or NULL.
|
||||
* Returns the length of the tag in bytes if not NULL.
|
||||
*
|
||||
* @return true on a match, false otherwise.
|
||||
*/
|
||||
bool bacnet_is_opening_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
|
||||
{
|
||||
bool match = false;
|
||||
uint8_t my_tag_number = 0;
|
||||
int len;
|
||||
|
||||
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
|
||||
if ((len > 0) && (my_tag_number == tag_number)) {
|
||||
if (IS_OPENING_TAG(apdu[0])) {
|
||||
match = true;
|
||||
if (tag_length) {
|
||||
*tag_length = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the tag does match and it
|
||||
* is an closing tag as well.
|
||||
@@ -733,6 +799,39 @@ bool decode_is_closing_tag_number(uint8_t *apdu, uint8_t tag_number)
|
||||
return (bool)(IS_CLOSING_TAG(apdu[0]) && (my_tag_number == tag_number));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the tag does match and it
|
||||
* is a closing tag as well.
|
||||
* As defined in clause 20.2.1.3.2 Constructed Data.
|
||||
*
|
||||
* @param apdu Pointer to the tag begin.
|
||||
* @param apdu_size - number of bytes in the buffer
|
||||
* @param tag_number Tag number, that has been decoded before.
|
||||
* @param tag_length Pointer to a variable, or NULL.
|
||||
* Returns the length of the tag in bytes if not NULL.
|
||||
*
|
||||
* @return true on a match, false otherwise.
|
||||
*/
|
||||
bool bacnet_is_closing_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length)
|
||||
{
|
||||
bool match = false;
|
||||
uint8_t my_tag_number = 0;
|
||||
int len;
|
||||
|
||||
len = bacnet_tag_number_decode(apdu, apdu_size, &my_tag_number);
|
||||
if ((len > 0) && (my_tag_number == tag_number)) {
|
||||
if (IS_CLOSING_TAG(apdu[0])) {
|
||||
match = true;
|
||||
if (tag_length) {
|
||||
*tag_length = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode an boolean value.
|
||||
* From clause 20.2.3 Encoding of a Boolean Value
|
||||
@@ -852,6 +951,60 @@ bool decode_boolean(uint32_t len_value)
|
||||
return boolean_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the Boolean Value when context encoded
|
||||
* From clause 20.2.3 Encoding of a Boolean Value
|
||||
* and 20.2.1 General Rules for Encoding BACnet Tags
|
||||
*
|
||||
* @note The Boolean datatype differs from the other datatypes
|
||||
* in that the encoding of a context-tagged Boolean value is not the
|
||||
* same as the encoding of an application-tagged Boolean value.
|
||||
* This is done so that the application-tagged value may be encoded
|
||||
* in a single octet, without a contents octet. While this same encoding
|
||||
* could have been used for the context-tagged case, doing
|
||||
* so would require that the context be known in order to distinguish
|
||||
* between a length or a value in the length/value/type field.
|
||||
* This was considered to be undesirable.
|
||||
*
|
||||
* @param apdu - buffer to hold the bytes
|
||||
* @param apdu_size - number of bytes in the buffer to decode
|
||||
* @param tag_value - context tag number expected
|
||||
* @param boolean_value - decoded Boolean Value, if decoded
|
||||
*
|
||||
* @return number of bytes decoded, zero if wrong tag number,
|
||||
* or #BACNET_STATUS_ERROR (-1) if malformed
|
||||
*/
|
||||
int bacnet_boolean_context_decode(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_value, bool *boolean_value)
|
||||
{
|
||||
int apdu_len = 0;
|
||||
int len = 0;
|
||||
|
||||
if (bacnet_is_context_tag_number(
|
||||
&apdu[apdu_len], apdu_size, tag_value, &len) &&
|
||||
!bacnet_is_closing_tag(&apdu[apdu_len], apdu_size)) {
|
||||
if (len > 0) {
|
||||
apdu_len += len;
|
||||
if (apdu_len < apdu_size) {
|
||||
if (boolean_value) {
|
||||
if (apdu[apdu_len]) {
|
||||
*boolean_value = true;
|
||||
} else {
|
||||
*boolean_value = false;
|
||||
}
|
||||
}
|
||||
apdu_len++;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a Null value.
|
||||
* From clause 20.2.2 Encoding of a Null Value
|
||||
|
||||
@@ -62,6 +62,9 @@ extern "C" {
|
||||
uint32_t len_value_type);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_is_context_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_is_opening_tag(uint8_t *apdu, uint32_t apdu_size);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_is_closing_tag(uint8_t *apdu, uint32_t apdu_size);
|
||||
@@ -96,6 +99,12 @@ bool bacnet_is_context_specific(uint8_t *apdu, uint32_t apdu_size);
|
||||
uint32_t * value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_is_opening_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacnet_is_closing_tag_number(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_number, int *tag_length);
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_tag_number_and_value_decode(
|
||||
uint8_t * apdu,
|
||||
uint32_t apdu_len_remaining,
|
||||
@@ -162,6 +171,11 @@ bool bacnet_is_context_specific(uint8_t *apdu, uint32_t apdu_size);
|
||||
uint8_t tag_number,
|
||||
bool * boolean_value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int bacnet_boolean_context_decode(
|
||||
uint8_t *apdu, uint32_t apdu_size, uint8_t tag_value,
|
||||
bool *boolean_value);
|
||||
|
||||
/* from clause 20.2.10 Encoding of a Bit String Value */
|
||||
/* returns the number of apdu bytes consumed */
|
||||
BACNET_STACK_EXPORT
|
||||
|
||||
@@ -1687,10 +1687,15 @@ typedef enum {
|
||||
ABORT_REASON_SEGMENTATION_NOT_SUPPORTED = 4,
|
||||
ABORT_REASON_SECURITY_ERROR = 5,
|
||||
ABORT_REASON_INSUFFICIENT_SECURITY = 6,
|
||||
ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE = 7,
|
||||
ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME = 8,
|
||||
ABORT_REASON_OUT_OF_RESOURCES = 9,
|
||||
ABORT_REASON_TSM_TIMEOUT = 10,
|
||||
ABORT_REASON_APDU_TOO_LONG = 11,
|
||||
/* Enumerated values 0-63 are reserved for definition by ASHRAE. */
|
||||
/* Enumerated values 64-255 may be used by others subject to */
|
||||
/* the procedures and constraints described in Clause 23. */
|
||||
MAX_BACNET_ABORT_REASON = 7,
|
||||
MAX_BACNET_ABORT_REASON = 12,
|
||||
ABORT_REASON_PROPRIETARY_FIRST = 64,
|
||||
ABORT_REASON_PROPRIETARY_LAST = 255
|
||||
} BACNET_ABORT_REASON;
|
||||
@@ -1706,10 +1711,11 @@ typedef enum {
|
||||
REJECT_REASON_TOO_MANY_ARGUMENTS = 7,
|
||||
REJECT_REASON_UNDEFINED_ENUMERATION = 8,
|
||||
REJECT_REASON_UNRECOGNIZED_SERVICE = 9,
|
||||
REJECT_REASON_INVALID_DATA_ENCODING = 10,
|
||||
/* Enumerated values 0-63 are reserved for definition by ASHRAE. */
|
||||
/* Enumerated values 64-255 may be used by others subject to */
|
||||
/* the procedures and constraints described in Clause 23. */
|
||||
MAX_BACNET_REJECT_REASON = 10,
|
||||
MAX_BACNET_REJECT_REASON = 11,
|
||||
REJECT_REASON_PROPRIETARY_FIRST = 64,
|
||||
REJECT_REASON_PROPRIETARY_LAST = 255
|
||||
} BACNET_REJECT_REASON;
|
||||
|
||||
@@ -1074,6 +1074,7 @@ INDTEXT_DATA bacnet_reject_reason_names[] = { { REJECT_REASON_OTHER, "Other" },
|
||||
{ REJECT_REASON_TOO_MANY_ARGUMENTS, "Too Many Arguments" },
|
||||
{ REJECT_REASON_UNDEFINED_ENUMERATION, "Undefined Enumeration" },
|
||||
{ REJECT_REASON_UNRECOGNIZED_SERVICE, "Unrecognized Service" },
|
||||
{ REJECT_REASON_INVALID_DATA_ENCODING, "invalid-data-encoding" },
|
||||
{ REJECT_REASON_PROPRIETARY_FIRST, "Proprietary" }, { 0, NULL } };
|
||||
|
||||
const char *bactext_reject_reason_name(unsigned index)
|
||||
@@ -1091,6 +1092,12 @@ INDTEXT_DATA bacnet_abort_reason_names[] = { { ABORT_REASON_OTHER, "Other" },
|
||||
{ ABORT_REASON_SEGMENTATION_NOT_SUPPORTED, "Segmentation Not Supported" },
|
||||
{ ABORT_REASON_SECURITY_ERROR, "Security Error" },
|
||||
{ ABORT_REASON_INSUFFICIENT_SECURITY, "Insufficient Security" },
|
||||
{ ABORT_REASON_WINDOW_SIZE_OUT_OF_RANGE, "window-size-out-of-range" },
|
||||
{ ABORT_REASON_APPLICATION_EXCEEDED_REPLY_TIME,
|
||||
"application-exceeded-reply-time" },
|
||||
{ ABORT_REASON_OUT_OF_RESOURCES, "out-of-resources" },
|
||||
{ ABORT_REASON_TSM_TIMEOUT, "tsm-timeout" },
|
||||
{ ABORT_REASON_APDU_TOO_LONG, "apdu-too-long" },
|
||||
{ ABORT_REASON_PROPRIETARY_FIRST, "Proprietary" }, { 0, NULL } };
|
||||
|
||||
const char *bactext_abort_reason_name(unsigned index)
|
||||
@@ -1104,7 +1111,8 @@ INDTEXT_DATA bacnet_error_class_names[] = { { ERROR_CLASS_DEVICE, "device" },
|
||||
{ ERROR_CLASS_OBJECT, "object" }, { ERROR_CLASS_PROPERTY, "property" },
|
||||
{ ERROR_CLASS_RESOURCES, "resources" },
|
||||
{ ERROR_CLASS_SECURITY, "security" }, { ERROR_CLASS_SERVICES, "services" },
|
||||
{ ERROR_CLASS_VT, "vt" }, { 0, NULL } };
|
||||
{ ERROR_CLASS_VT, "vt" }, { ERROR_CLASS_COMMUNICATION, "communication" },
|
||||
{ 0, NULL } };
|
||||
|
||||
const char *bactext_error_class_name(unsigned index)
|
||||
{
|
||||
|
||||
@@ -1196,15 +1196,24 @@ void Analog_Output_Write_Present_Value_Callback_Set(
|
||||
/**
|
||||
* @brief Creates a Analog Value object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the object-instance was created
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool Analog_Output_Create(uint32_t object_instance)
|
||||
uint32_t Analog_Output_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
unsigned priority = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -1227,13 +1236,17 @@ bool Analog_Output_Create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -191,7 +191,7 @@ extern "C" {
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Analog_Output_Create(
|
||||
uint32_t Analog_Output_Create(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Analog_Output_Delete(
|
||||
|
||||
@@ -977,16 +977,25 @@ bool bacfile_read_ack_record_data(
|
||||
|
||||
|
||||
/**
|
||||
* @brief Creates an object
|
||||
* @brief Creates a File object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the object-instance was created
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool bacfile_create(uint32_t object_instance)
|
||||
uint32_t bacfile_create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -1002,13 +1011,17 @@ bool bacfile_create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,7 +175,7 @@ extern "C" {
|
||||
uint32_t buffer_size);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacfile_create(
|
||||
uint32_t bacfile_create(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool bacfile_delete(
|
||||
|
||||
@@ -1170,16 +1170,25 @@ void Binary_Output_Write_Present_Value_Callback_Set(
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determines a object write-enabled flag state
|
||||
* @brief Creates a Binary Output object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return write-enabled status flag
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool Binary_Output_Create(uint32_t object_instance)
|
||||
uint32_t Binary_Output_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -1194,13 +1203,17 @@ bool Binary_Output_Create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -228,7 +228,7 @@ extern "C" {
|
||||
BACNET_WRITE_PROPERTY_DATA * wp_data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Binary_Output_Create(
|
||||
uint32_t Binary_Output_Create(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Binary_Output_Delete(
|
||||
|
||||
@@ -117,7 +117,8 @@ static object_functions_t Object_Table[] = {
|
||||
Device_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
|
||||
NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
|
||||
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
|
||||
@@ -125,7 +126,8 @@ static object_functions_t Object_Table[] = {
|
||||
Network_Port_Write_Property, Network_Port_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#endif
|
||||
{ MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */,
|
||||
NULL /* Index_To_Instance */, NULL /* Valid_Instance */,
|
||||
@@ -134,7 +136,8 @@ static object_functions_t Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */,
|
||||
NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
};
|
||||
|
||||
/** Glue function to let the Device object, when called by a handler,
|
||||
|
||||
@@ -827,15 +827,25 @@ void Color_Write_Disable(uint32_t object_instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Color object
|
||||
* @brief Creates a Color object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool Color_Create(uint32_t object_instance)
|
||||
uint32_t Color_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -856,13 +866,17 @@ bool Color_Create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -119,7 +119,7 @@ BACNET_STACK_EXPORT
|
||||
void Color_Write_Disable(uint32_t instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Create(uint32_t object_instance);
|
||||
uint32_t Color_Create(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Delete(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
|
||||
@@ -1021,15 +1021,25 @@ void Color_Temperature_Write_Disable(uint32_t object_instance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Color object
|
||||
* @brief Creates a Color Temperature object
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool Color_Temperature_Create(uint32_t object_instance)
|
||||
uint32_t Color_Temperature_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -1051,13 +1061,17 @@ bool Color_Temperature_Create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -151,7 +151,7 @@ BACNET_STACK_EXPORT
|
||||
void Color_Temperature_Write_Disable(uint32_t instance);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Create(uint32_t object_instance);
|
||||
uint32_t Color_Temperature_Create(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Color_Temperature_Delete(uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
|
||||
@@ -98,7 +98,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */,
|
||||
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
|
||||
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
|
||||
NULL /* Remove_List_Element */ },
|
||||
NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#if (BACNET_PROTOCOL_REVISION >= 17)
|
||||
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
|
||||
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
|
||||
@@ -106,7 +107,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
Network_Port_Write_Property, Network_Port_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#endif
|
||||
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
|
||||
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
|
||||
@@ -115,14 +117,16 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value,
|
||||
Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_ANALOG_OUTPUT, Analog_Output_Init, Analog_Output_Count,
|
||||
Analog_Output_Index_To_Instance, Analog_Output_Valid_Instance,
|
||||
Analog_Output_Object_Name, Analog_Output_Read_Property,
|
||||
Analog_Output_Write_Property, Analog_Output_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Analog_Output_Create, Analog_Output_Delete},
|
||||
{ OBJECT_ANALOG_VALUE, Analog_Value_Init, Analog_Value_Count,
|
||||
Analog_Value_Index_To_Instance, Analog_Value_Valid_Instance,
|
||||
Analog_Value_Object_Name, Analog_Value_Read_Property,
|
||||
@@ -130,7 +134,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
Analog_Value_Encode_Value_List, Analog_Value_Change_Of_Value,
|
||||
Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count,
|
||||
Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance,
|
||||
Binary_Input_Object_Name, Binary_Input_Read_Property,
|
||||
@@ -138,21 +143,24 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
Binary_Input_Encode_Value_List, Binary_Input_Change_Of_Value,
|
||||
Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_BINARY_OUTPUT, Binary_Output_Init, Binary_Output_Count,
|
||||
Binary_Output_Index_To_Instance, Binary_Output_Valid_Instance,
|
||||
Binary_Output_Object_Name, Binary_Output_Read_Property,
|
||||
Binary_Output_Write_Property, Binary_Output_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Binary_Output_Create, Binary_Output_Delete},
|
||||
{ OBJECT_BINARY_VALUE, Binary_Value_Init, Binary_Value_Count,
|
||||
Binary_Value_Index_To_Instance, Binary_Value_Valid_Instance,
|
||||
Binary_Value_Object_Name, Binary_Value_Read_Property,
|
||||
Binary_Value_Write_Property, Binary_Value_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init,
|
||||
CharacterString_Value_Count, CharacterString_Value_Index_To_Instance,
|
||||
CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name,
|
||||
@@ -163,20 +171,23 @@ static object_functions_t My_Object_Table[] = {
|
||||
CharacterString_Value_Change_Of_Value,
|
||||
CharacterString_Value_Change_Of_Value_Clear,
|
||||
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
|
||||
NULL /* Remove_List_Element */ },
|
||||
NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_COMMAND, Command_Init, Command_Count, Command_Index_To_Instance,
|
||||
Command_Valid_Instance, Command_Object_Name, Command_Read_Property,
|
||||
Command_Write_Property, Command_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_INTEGER_VALUE, Integer_Value_Init, Integer_Value_Count,
|
||||
Integer_Value_Index_To_Instance, Integer_Value_Valid_Instance,
|
||||
Integer_Value_Object_Name, Integer_Value_Read_Property,
|
||||
Integer_Value_Write_Property, Integer_Value_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
{ OBJECT_NOTIFICATION_CLASS, Notification_Class_Init,
|
||||
Notification_Class_Count, Notification_Class_Index_To_Instance,
|
||||
@@ -186,7 +197,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
Notification_Class_Add_List_Element,
|
||||
Notification_Class_Remove_List_Element },
|
||||
Notification_Class_Remove_List_Element,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#endif
|
||||
{ OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count,
|
||||
Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance,
|
||||
@@ -194,21 +206,24 @@ static object_functions_t My_Object_Table[] = {
|
||||
Life_Safety_Point_Write_Property, Life_Safety_Point_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_LOAD_CONTROL, Load_Control_Init, Load_Control_Count,
|
||||
Load_Control_Index_To_Instance, Load_Control_Valid_Instance,
|
||||
Load_Control_Object_Name, Load_Control_Read_Property,
|
||||
Load_Control_Write_Property, Load_Control_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_MULTI_STATE_INPUT, Multistate_Input_Init, Multistate_Input_Count,
|
||||
Multistate_Input_Index_To_Instance, Multistate_Input_Valid_Instance,
|
||||
Multistate_Input_Object_Name, Multistate_Input_Read_Property,
|
||||
Multistate_Input_Write_Property, Multistate_Input_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init,
|
||||
Multistate_Output_Count, Multistate_Output_Index_To_Instance,
|
||||
Multistate_Output_Valid_Instance, Multistate_Output_Object_Name,
|
||||
@@ -216,7 +231,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
Multistate_Output_Property_Lists, NULL /* ReadRangeInfo */,
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Multistate_Output_Create, Multistate_Output_Delete},
|
||||
{ OBJECT_MULTI_STATE_VALUE, Multistate_Value_Init, Multistate_Value_Count,
|
||||
Multistate_Value_Index_To_Instance, Multistate_Value_Valid_Instance,
|
||||
Multistate_Value_Object_Name, Multistate_Value_Read_Property,
|
||||
@@ -224,14 +240,16 @@ static object_functions_t My_Object_Table[] = {
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
Multistate_Value_Encode_Value_List, Multistate_Value_Change_Of_Value,
|
||||
Multistate_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_TRENDLOG, Trend_Log_Init, Trend_Log_Count,
|
||||
Trend_Log_Index_To_Instance, Trend_Log_Valid_Instance,
|
||||
Trend_Log_Object_Name, Trend_Log_Read_Property,
|
||||
Trend_Log_Write_Property, Trend_Log_Property_Lists, TrendLogGetRRInfo,
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#if (BACNET_PROTOCOL_REVISION >= 14)
|
||||
{ OBJECT_LIGHTING_OUTPUT, Lighting_Output_Init, Lighting_Output_Count,
|
||||
Lighting_Output_Index_To_Instance, Lighting_Output_Valid_Instance,
|
||||
@@ -239,13 +257,15 @@ static object_functions_t My_Object_Table[] = {
|
||||
Lighting_Output_Write_Property, Lighting_Output_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_CHANNEL, Channel_Init, Channel_Count, Channel_Index_To_Instance,
|
||||
Channel_Valid_Instance, Channel_Object_Name, Channel_Read_Property,
|
||||
Channel_Write_Property, Channel_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
#endif
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
{ OBJECT_COLOR, Color_Init, Color_Count, Color_Index_To_Instance,
|
||||
@@ -253,14 +273,16 @@ static object_functions_t My_Object_Table[] = {
|
||||
Color_Write_Property, Color_Property_Lists, NULL /* ReadRangeInfo */,
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Color_Create, Color_Delete},
|
||||
{ OBJECT_COLOR_TEMPERATURE, Color_Temperature_Init, Color_Temperature_Count,
|
||||
Color_Temperature_Index_To_Instance, Color_Temperature_Valid_Instance,
|
||||
Color_Temperature_Object_Name, Color_Temperature_Read_Property,
|
||||
Color_Temperature_Write_Property, Color_Temperature_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
Color_Temperature_Create, Color_Temperature_Delete},
|
||||
#endif
|
||||
#if defined(BACFILE)
|
||||
{ OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance,
|
||||
@@ -268,7 +290,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
bacfile_write_property, BACfile_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
bacfile_create, bacfile_delete},
|
||||
#endif
|
||||
{ OBJECT_OCTETSTRING_VALUE, OctetString_Value_Init, OctetString_Value_Count,
|
||||
OctetString_Value_Index_To_Instance, OctetString_Value_Valid_Instance,
|
||||
@@ -276,7 +299,8 @@ static object_functions_t My_Object_Table[] = {
|
||||
OctetString_Value_Write_Property, OctetString_Value_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_POSITIVE_INTEGER_VALUE, PositiveInteger_Value_Init,
|
||||
PositiveInteger_Value_Count, PositiveInteger_Value_Index_To_Instance,
|
||||
PositiveInteger_Value_Valid_Instance, PositiveInteger_Value_Object_Name,
|
||||
@@ -285,28 +309,32 @@ static object_functions_t My_Object_Table[] = {
|
||||
PositiveInteger_Value_Property_Lists, NULL /* ReadRangeInfo */,
|
||||
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
|
||||
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_SCHEDULE, Schedule_Init, Schedule_Count,
|
||||
Schedule_Index_To_Instance, Schedule_Valid_Instance,
|
||||
Schedule_Object_Name, Schedule_Read_Property, Schedule_Write_Property,
|
||||
Schedule_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */,
|
||||
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
|
||||
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
|
||||
NULL /* Remove_List_Element */ },
|
||||
NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ OBJECT_ACCUMULATOR, Accumulator_Init, Accumulator_Count,
|
||||
Accumulator_Index_To_Instance, Accumulator_Valid_Instance,
|
||||
Accumulator_Object_Name, Accumulator_Read_Property,
|
||||
Accumulator_Write_Property, Accumulator_Property_Lists,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
{ MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */,
|
||||
NULL /* Index_To_Instance */, NULL /* Valid_Instance */,
|
||||
NULL /* Object_Name */, NULL /* Read_Property */,
|
||||
NULL /* Write_Property */, NULL /* Property_Lists */,
|
||||
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
|
||||
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }
|
||||
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
|
||||
NULL /* Create */, NULL /* Delete */ },
|
||||
};
|
||||
|
||||
/** Glue function to let the Device object, when called by a handler,
|
||||
@@ -1892,6 +1920,108 @@ void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a child object, if supported
|
||||
* @ingroup ObjHelpers
|
||||
* @param data - CreateObject data, including error codes if failures
|
||||
* @return true if object has been created
|
||||
*/
|
||||
bool Device_Create_Object(
|
||||
BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_functions *pObject = NULL;
|
||||
uint32_t object_instance;
|
||||
|
||||
pObject = Device_Objects_Find_Functions(data->object_type);
|
||||
if (pObject != NULL) {
|
||||
if (!pObject->Object_Create) {
|
||||
/* The device supports the object type and may have
|
||||
sufficient space, but does not support the creation of the
|
||||
object for some other reason.*/
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
|
||||
} else if (pObject->Object_Valid_Instance &&
|
||||
pObject->Object_Valid_Instance(data->object_instance)) {
|
||||
/* The object being created already exists */
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
|
||||
} else {
|
||||
if (data->list_of_initial_values) {
|
||||
/* FIXME: add support for writing to list of initial values */
|
||||
/* A property specified by the Property_Identifier in the
|
||||
List of Initial Values does not support initialization
|
||||
during the CreateObject service. */
|
||||
data->first_failed_element_number = 1;
|
||||
data->error_class = ERROR_CLASS_PROPERTY;
|
||||
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
|
||||
/* and the object shall not be created */
|
||||
} else {
|
||||
object_instance = pObject->Object_Create(data->object_instance);
|
||||
if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* The device cannot allocate the space needed
|
||||
for the new object.*/
|
||||
data->error_class = ERROR_CLASS_RESOURCES;
|
||||
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
|
||||
} else {
|
||||
/* required by ACK */
|
||||
data->object_instance = object_instance;
|
||||
status = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* The device does not support the specified object type. */
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deletes a child object, if supported
|
||||
* @ingroup ObjHelpers
|
||||
* @param data - DeleteObject data, including error codes if failures
|
||||
* @return true if object has been deleted
|
||||
*/
|
||||
bool Device_Delete_Object(
|
||||
BACNET_DELETE_OBJECT_DATA *data)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_functions *pObject = NULL;
|
||||
|
||||
pObject = Device_Objects_Find_Functions(data->object_type);
|
||||
if (pObject != NULL) {
|
||||
if (!pObject->Object_Delete) {
|
||||
/* The device supports the object type
|
||||
but does not support the deletion of the
|
||||
object for some reason.*/
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
|
||||
} else if (pObject->Object_Valid_Instance &&
|
||||
pObject->Object_Valid_Instance(data->object_instance)) {
|
||||
/* The object being deleted must already exist */
|
||||
status = pObject->Object_Delete(data->object_instance);
|
||||
if (!status) {
|
||||
/* The object exists but cannot be deleted. */
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
|
||||
}
|
||||
} else {
|
||||
/* The object to be deleted does not exist. */
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
|
||||
}
|
||||
} else {
|
||||
/* The device does not support the specified object type. */
|
||||
data->error_class = ERROR_CLASS_OBJECT;
|
||||
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(INTRINSIC_REPORTING)
|
||||
void Device_local_reporting(void)
|
||||
{
|
||||
@@ -1966,17 +2096,13 @@ void Device_Init(object_functions_t *object_table)
|
||||
pObject++;
|
||||
}
|
||||
/* create some dynamically created objects as examples */
|
||||
Analog_Output_Create(1);
|
||||
Binary_Output_Create(1);
|
||||
Multistate_Output_Create(1);
|
||||
#if (BACNET_PROTOCOL_REVISION >= 24)
|
||||
Color_Create(1);
|
||||
Color_Temperature_Create(1);
|
||||
#endif
|
||||
#if defined(BACFILE)
|
||||
bacfile_create(1);
|
||||
bacfile_pathname_set(1, "temp_1.txt");
|
||||
#endif
|
||||
pObject = Object_Table;
|
||||
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
|
||||
if (pObject->Object_Create) {
|
||||
pObject->Object_Create(BACNET_MAX_INSTANCE);
|
||||
}
|
||||
pObject++;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/create_object.h"
|
||||
#include "bacnet/delete_object.h"
|
||||
#include "bacnet/list_element.h"
|
||||
#include "bacnet/wp.h"
|
||||
#include "bacnet/rd.h"
|
||||
@@ -168,6 +170,8 @@ typedef struct object_functions {
|
||||
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
|
||||
list_element_function Object_Add_List_Element;
|
||||
list_element_function Object_Remove_List_Element;
|
||||
create_object_function Object_Create;
|
||||
delete_object_function Object_Delete;
|
||||
} object_functions_t;
|
||||
|
||||
/* String Lengths - excluding any nul terminator */
|
||||
@@ -316,6 +320,13 @@ extern "C" {
|
||||
BACNET_ARRAY_INDEX array_index,
|
||||
uint8_t *apdu);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Device_Create_Object(
|
||||
BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Device_Delete_Object(
|
||||
BACNET_DELETE_OBJECT_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
unsigned Device_Count(
|
||||
void);
|
||||
|
||||
@@ -1152,15 +1152,24 @@ void Multistate_Output_Write_Present_Value_Callback_Set(
|
||||
/**
|
||||
* @brief Creates a new object and adds it to the object list
|
||||
* @param object_instance - object-instance number of the object
|
||||
* @return true if the object is created
|
||||
* @return the object-instance that was created, or BACNET_MAX_INSTANCE
|
||||
*/
|
||||
bool Multistate_Output_Create(uint32_t object_instance)
|
||||
uint32_t Multistate_Output_Create(uint32_t object_instance)
|
||||
{
|
||||
bool status = false;
|
||||
struct object_data *pObject = NULL;
|
||||
int index = 0;
|
||||
unsigned priority = 0;
|
||||
|
||||
if (object_instance > BACNET_MAX_INSTANCE) {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
} else if (object_instance == BACNET_MAX_INSTANCE) {
|
||||
/* wildcard instance */
|
||||
/* the Object_Identifier property of the newly created object
|
||||
shall be initialized to a value that is unique within the
|
||||
responding BACnet-user device. The method used to generate
|
||||
the object identifier is a local matter.*/
|
||||
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
|
||||
}
|
||||
pObject = Keylist_Data(Object_List, object_instance);
|
||||
if (!pObject) {
|
||||
pObject = calloc(1, sizeof(struct object_data));
|
||||
@@ -1178,13 +1187,17 @@ bool Multistate_Output_Create(uint32_t object_instance)
|
||||
/* add to list */
|
||||
index = Keylist_Data_Add(Object_List, object_instance, pObject);
|
||||
if (index >= 0) {
|
||||
status = true;
|
||||
Device_Inc_Database_Revision();
|
||||
} else {
|
||||
free(pObject);
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
} else {
|
||||
return BACNET_MAX_INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return object_instance;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -174,7 +174,7 @@ extern "C" {
|
||||
uint32_t value);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
bool Multistate_Output_Create(
|
||||
uint32_t Multistate_Output_Create(
|
||||
uint32_t object_instance);
|
||||
BACNET_STACK_EXPORT
|
||||
bool Multistate_Output_Delete(
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief CreateObject service application handlers
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/reject.h"
|
||||
#include "bacnet/create_object.h"
|
||||
/* basic objects, services, TSM, and datalink */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
/**
|
||||
* @brief Handler for a CreateObject service request.
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* via call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - a SimpleACK if Device_Create_Object() succeeds
|
||||
* - an Error if Device_Create_Object() fails
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
void handler_create_object(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
{
|
||||
BACNET_CREATE_OBJECT_DATA data = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
BACNET_ADDRESS my_address = { 0 };
|
||||
int len = 0;
|
||||
bool status = true;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
debug_perror("CreateObject: Received Request!\n");
|
||||
if (service_data->segmented_message) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
debug_perror("CreateObject: Segmented message. Sending Abort!\n");
|
||||
status = false;
|
||||
}
|
||||
if (status) {
|
||||
/* decode the service request only */
|
||||
len = create_object_decode_service_request(
|
||||
service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
debug_perror("CreateObject: type=%lu instance=%lu\n",
|
||||
(unsigned long)data.object_type,
|
||||
(unsigned long)data.object_instance);
|
||||
} else {
|
||||
debug_perror("CreateObject: Unable to decode request!\n");
|
||||
}
|
||||
if (len <= 0) {
|
||||
/* bad decoding or something we didn't understand */
|
||||
if (len == BACNET_STATUS_ABORT) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
abort_convert_error_code(data.error_code), true);
|
||||
debug_perror("CreateObject: Sending Abort!\n");
|
||||
} else if (len == BACNET_STATUS_REJECT) {
|
||||
len = reject_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
reject_convert_error_code(data.error_code));
|
||||
debug_perror("CreateObject: Sending Reject!\n");
|
||||
}
|
||||
} else {
|
||||
if (Device_Create_Object(&data)) {
|
||||
len =
|
||||
create_object_ack_encode(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
debug_perror("CreateObject: Sending ACK!\n");
|
||||
} else {
|
||||
len = create_object_error_ack_encode(
|
||||
&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, &data);
|
||||
debug_perror("CreateObject: Sending Error!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
/* Send PDU */
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(
|
||||
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
}
|
||||
if (bytes_sent <= 0) {
|
||||
debug_perror(
|
||||
"CreateObject: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for CreateObject service handlers
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef HANDLER_CREATE_OBJECT_H
|
||||
#define HANDLER_CREATE_OBJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void handler_create_object(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief DeleteObject service application handlers
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/reject.h"
|
||||
#include "bacnet/delete_object.h"
|
||||
/* basic objects, services, TSM, and datalink */
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
/**
|
||||
* @brief Handler for a DeleteObject Service request.
|
||||
* This handler will be invoked by apdu_handler() if it has been enabled
|
||||
* via call to apdu_set_confirmed_handler().
|
||||
* This handler builds a response packet, which is
|
||||
* - an Abort if
|
||||
* - the message is segmented
|
||||
* - if decoding fails
|
||||
* - a SimpleACK if Device_Delete_Object() succeeds
|
||||
* - an Error if Device_Delete_Object() fails
|
||||
*
|
||||
* @param service_request [in] The contents of the service request.
|
||||
* @param service_len [in] The length of the service_request.
|
||||
* @param src [in] BACNET_ADDRESS of the source of the message
|
||||
* @param service_data [in] The BACNET_CONFIRMED_SERVICE_DATA information
|
||||
* decoded from the APDU header of this message.
|
||||
*/
|
||||
void handler_delete_object(uint8_t *service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS *src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data)
|
||||
{
|
||||
BACNET_DELETE_OBJECT_DATA data = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
BACNET_ADDRESS my_address = { 0 };
|
||||
int len = 0;
|
||||
bool status = true;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], src, &my_address, &npdu_data);
|
||||
debug_perror("DeleteObject: Received Request!\n");
|
||||
if (service_data->segmented_message) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_SEGMENTATION_NOT_SUPPORTED,
|
||||
true);
|
||||
debug_perror("DeleteObject: Segmented message. Sending Abort!\n");
|
||||
status = false;
|
||||
}
|
||||
if (status) {
|
||||
/* decode the service request only */
|
||||
len = delete_object_decode_service_request(
|
||||
service_request, service_len, &data);
|
||||
if (len > 0) {
|
||||
debug_perror("DeleteObject: type=%lu instance=%lu\n",
|
||||
(unsigned long)data.object_type,
|
||||
(unsigned long)data.object_instance);
|
||||
} else {
|
||||
debug_perror("DeleteObject: Unable to decode request!\n");
|
||||
}
|
||||
/* bad decoding or something we didn't understand - send an abort */
|
||||
if (len <= 0) {
|
||||
len = abort_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id, ABORT_REASON_OTHER, true);
|
||||
debug_perror("DeleteObject: Bad Encoding. Sending Abort!\n");
|
||||
status = false;
|
||||
}
|
||||
if (status) {
|
||||
if (Device_Delete_Object(&data)) {
|
||||
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_DELETE_OBJECT);
|
||||
debug_perror("DeleteObject: Sending Simple Ack!\n");
|
||||
} else {
|
||||
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
|
||||
service_data->invoke_id,
|
||||
SERVICE_CONFIRMED_DELETE_OBJECT,
|
||||
data.error_class, data.error_code);
|
||||
debug_perror("DeleteObject: Sending Error!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (len > 0) {
|
||||
/* Send PDU */
|
||||
pdu_len += len;
|
||||
bytes_sent = datalink_send_pdu(
|
||||
src, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
}
|
||||
if (bytes_sent <= 0) {
|
||||
debug_perror(
|
||||
"DeleteObject: Failed to send PDU (%s)!\n", strerror(errno));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for DeleteObject service handlers
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef HANDLER_DELETE_OBJECT_H
|
||||
#define HANDLER_DELETE_OBJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
void handler_delete_object(
|
||||
uint8_t * service_request,
|
||||
uint16_t service_len,
|
||||
BACNET_ADDRESS * src,
|
||||
BACNET_CONFIRMED_SERVICE_DATA *service_data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief CreateObject service initiation
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/create_object.h"
|
||||
#include "bacnet/whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
/**
|
||||
* @brief Send a CreateObject service message
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param object_type [in] Type of the object whose property is to be written.
|
||||
* @param object_instance [in] Instance # of the object to be written.
|
||||
* @return invoke id of outgoing message, or 0 on failure.
|
||||
* @return the invoke ID for confirmed request, or zero on failure
|
||||
*/
|
||||
uint8_t Send_Create_Object_Request_Data(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE *values)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_CREATE_OBJECT_DATA data = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
uint8_t service = SERVICE_CONFIRMED_CREATE_OBJECT;
|
||||
|
||||
if (!dcc_communication_enabled()) {
|
||||
return 0;
|
||||
}
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
/* is there a tsm available? */
|
||||
if (status) {
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
}
|
||||
if (invoke_id) {
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data);
|
||||
/* encode the APDU header portion of the packet */
|
||||
Handler_Transmit_Buffer[pdu_len++] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
||||
Handler_Transmit_Buffer[pdu_len++] =
|
||||
encode_max_segs_max_apdu(0, MAX_APDU);
|
||||
Handler_Transmit_Buffer[pdu_len++] = invoke_id;
|
||||
Handler_Transmit_Buffer[pdu_len++] = service;
|
||||
/* encode the APDU service */
|
||||
data.object_type = object_type;
|
||||
data.object_instance = object_instance;
|
||||
data.list_of_initial_values = values;
|
||||
/* get the length of the APDU */
|
||||
len = create_object_encode_service_request(NULL, &data);
|
||||
pdu_len += len;
|
||||
/* will it fit in the sender and our buffer?
|
||||
note: if there is a bottleneck router in between
|
||||
us and the destination, we won't know unless
|
||||
we have a way to check for that and update the
|
||||
max_apdu in the address binding table. */
|
||||
if (((unsigned)pdu_len < max_apdu) &&
|
||||
(pdu_len < sizeof(Handler_Transmit_Buffer))) {
|
||||
/* shift back to the service portion of the buffer */
|
||||
pdu_len -= len;
|
||||
len = create_object_encode_service_request(
|
||||
&Handler_Transmit_Buffer[pdu_len], &data);
|
||||
pdu_len += len;
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len);
|
||||
bytes_sent = datalink_send_pdu(
|
||||
&dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
debug_perror("%s service: Failed to Send %i/%i (%s)!\n",
|
||||
bactext_confirmed_service_name(service), bytes_sent,
|
||||
pdu_len, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
debug_perror("%s service: Failed to Send "
|
||||
"(exceeds destination maximum APDU)!\n",
|
||||
bactext_confirmed_service_name(service));
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send a CreateObject service message
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param object_type [in] Type of the object whose property is to be written.
|
||||
* @param object_instance [in] Instance # of the object to be written.
|
||||
* @return invoke id of outgoing message, or 0 on failure.
|
||||
* @return the invoke ID for confirmed request, or zero on failure
|
||||
*/
|
||||
uint8_t Send_Create_Object_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance)
|
||||
{
|
||||
return Send_Create_Object_Request_Data(device_id, object_type, object_instance, NULL);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief CreateObject service initiation
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef SEND_CREATE_OBJECT_H
|
||||
#define SEND_CREATE_OBJECT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t Send_Create_Object_Request_Data(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance,
|
||||
BACNET_PROPERTY_VALUE *values);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t Send_Create_Object_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief DeleteObject service initiation
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "bacnet/config.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/apdu.h"
|
||||
#include "bacnet/npdu.h"
|
||||
#include "bacnet/abort.h"
|
||||
#include "bacnet/bactext.h"
|
||||
#include "bacnet/dcc.h"
|
||||
#include "bacnet/delete_object.h"
|
||||
#include "bacnet/whois.h"
|
||||
/* some demo stuff needed */
|
||||
#include "bacnet/basic/binding/address.h"
|
||||
#include "bacnet/basic/object/device.h"
|
||||
#include "bacnet/basic/services.h"
|
||||
#include "bacnet/basic/sys/debug.h"
|
||||
#include "bacnet/basic/tsm/tsm.h"
|
||||
#include "bacnet/datalink/datalink.h"
|
||||
|
||||
/**
|
||||
* @brief Send a CreateObject service message
|
||||
* @param device_id [in] ID of the destination device
|
||||
* @param object_type [in] Type of the object whose property is to be written.
|
||||
* @param object_instance [in] Instance # of the object to be written.
|
||||
* @return invoke id of outgoing message, or 0 on failure.
|
||||
* @return the invoke ID for confirmed request, or zero on failure
|
||||
*/
|
||||
uint8_t Send_Delete_Object_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance)
|
||||
{
|
||||
BACNET_ADDRESS dest;
|
||||
BACNET_ADDRESS my_address;
|
||||
unsigned max_apdu = 0;
|
||||
uint8_t invoke_id = 0;
|
||||
bool status = false;
|
||||
int len = 0;
|
||||
int pdu_len = 0;
|
||||
int bytes_sent = 0;
|
||||
BACNET_DELETE_OBJECT_DATA data = { 0 };
|
||||
BACNET_NPDU_DATA npdu_data = { 0 };
|
||||
uint8_t service = SERVICE_CONFIRMED_DELETE_OBJECT;
|
||||
|
||||
if (!dcc_communication_enabled()) {
|
||||
return 0;
|
||||
}
|
||||
/* is the device bound? */
|
||||
status = address_get_by_device(device_id, &max_apdu, &dest);
|
||||
/* is there a tsm available? */
|
||||
if (status) {
|
||||
invoke_id = tsm_next_free_invokeID();
|
||||
}
|
||||
if (invoke_id) {
|
||||
/* encode the NPDU portion of the packet */
|
||||
datalink_get_my_address(&my_address);
|
||||
npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
|
||||
pdu_len = npdu_encode_pdu(
|
||||
&Handler_Transmit_Buffer[0], &dest, &my_address, &npdu_data);
|
||||
/* encode the APDU header portion of the packet */
|
||||
Handler_Transmit_Buffer[pdu_len++] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST;
|
||||
Handler_Transmit_Buffer[pdu_len++] =
|
||||
encode_max_segs_max_apdu(0, MAX_APDU);
|
||||
Handler_Transmit_Buffer[pdu_len++] = invoke_id;
|
||||
Handler_Transmit_Buffer[pdu_len++] = service;
|
||||
/* encode the APDU service */
|
||||
data.object_type = object_type;
|
||||
data.object_instance = object_instance;
|
||||
/* get the length of the APDU */
|
||||
len = delete_object_encode_service_request(NULL, &data);
|
||||
pdu_len += len;
|
||||
/* will it fit in the sender and our buffer?
|
||||
note: if there is a bottleneck router in between
|
||||
us and the destination, we won't know unless
|
||||
we have a way to check for that and update the
|
||||
max_apdu in the address binding table. */
|
||||
if (((unsigned)pdu_len < max_apdu) &&
|
||||
(pdu_len < sizeof(Handler_Transmit_Buffer))) {
|
||||
/* shift back to the service portion of the buffer */
|
||||
pdu_len -= len;
|
||||
len = delete_object_encode_service_request(
|
||||
&Handler_Transmit_Buffer[pdu_len], &data);
|
||||
pdu_len += len;
|
||||
tsm_set_confirmed_unsegmented_transaction(invoke_id, &dest,
|
||||
&npdu_data, &Handler_Transmit_Buffer[0], (uint16_t)pdu_len);
|
||||
bytes_sent = datalink_send_pdu(
|
||||
&dest, &npdu_data, &Handler_Transmit_Buffer[0], pdu_len);
|
||||
if (bytes_sent <= 0) {
|
||||
debug_perror("%s service: Failed to Send %i/%i (%s)!\n",
|
||||
bactext_confirmed_service_name(service), bytes_sent,
|
||||
pdu_len, strerror(errno));
|
||||
}
|
||||
} else {
|
||||
tsm_free_invoke_id(invoke_id);
|
||||
invoke_id = 0;
|
||||
debug_perror("%s service: Failed to Send "
|
||||
"(exceeds destination maximum APDU)!\n",
|
||||
bactext_confirmed_service_name(service));
|
||||
}
|
||||
}
|
||||
|
||||
return invoke_id;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief DeleteObject service initiation
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef SEND_DELETE_OBJECT_H
|
||||
#define SEND_DELETE_OBJECT_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/apdu.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
uint8_t Send_Delete_Object_Request(
|
||||
uint32_t device_id,
|
||||
BACNET_OBJECT_TYPE object_type,
|
||||
uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif
|
||||
@@ -41,7 +41,9 @@
|
||||
#include "bacnet/basic/service/h_awf.h"
|
||||
#include "bacnet/basic/service/h_ccov.h"
|
||||
#include "bacnet/basic/service/h_cov.h"
|
||||
#include "bacnet/basic/service/h_create_object.h"
|
||||
#include "bacnet/basic/service/h_dcc.h"
|
||||
#include "bacnet/basic/service/h_delete_object.h"
|
||||
#include "bacnet/basic/service/h_gas_a.h"
|
||||
#include "bacnet/basic/service/h_get_alarm_sum.h"
|
||||
#include "bacnet/basic/service/h_getevent.h"
|
||||
@@ -73,7 +75,9 @@
|
||||
#include "bacnet/basic/service/s_awfs.h"
|
||||
#include "bacnet/basic/service/s_cevent.h"
|
||||
#include "bacnet/basic/service/s_cov.h"
|
||||
#include "bacnet/basic/service/s_create_object.h"
|
||||
#include "bacnet/basic/service/s_dcc.h"
|
||||
#include "bacnet/basic/service/s_delete_object.h"
|
||||
#include "bacnet/basic/service/s_error.h"
|
||||
#include "bacnet/basic/service/s_get_alarm_sum.h"
|
||||
#include "bacnet/basic/service/s_get_event.h"
|
||||
|
||||
@@ -18,12 +18,17 @@
|
||||
#ifndef BACNET_SYS_PLATFORM_H
|
||||
#define BACNET_SYS_PLATFORM_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifndef islessgreater
|
||||
#define islessgreater(x, y) ((x) < (y) || (x) > (y))
|
||||
#endif
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(array) ((size_t)(sizeof(array) / sizeof((array)[0])))
|
||||
#endif
|
||||
|
||||
/* marking some code as 'deprecated' */
|
||||
# if defined(_MSC_VER)
|
||||
# define BACNET_STACK_DEPRECATED(message) __declspec(deprecated(message))
|
||||
|
||||
+238
-243
@@ -50,88 +50,94 @@ Unconfirmed COV Notification
|
||||
*/
|
||||
|
||||
/**
|
||||
* Encode APDU for notification.
|
||||
*
|
||||
* @param apdu Pointer to the buffer.
|
||||
* @brief Encode APDU for COV Notification.
|
||||
* @param apdu Pointer to the buffer, or NULL for length
|
||||
* @param data Pointer to the data to encode.
|
||||
*
|
||||
* @return bytes encoded or zero on error.
|
||||
*/
|
||||
static int notify_encode_apdu(
|
||||
uint8_t *apdu, unsigned max_apdu_len, BACNET_COV_DATA *data)
|
||||
int cov_notify_encode_apdu(uint8_t *apdu, BACNET_COV_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
|
||||
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
|
||||
|
||||
(void)max_apdu_len;
|
||||
if (apdu) {
|
||||
/* tag 0 - subscriberProcessIdentifier */
|
||||
len = encode_context_unsigned(
|
||||
&apdu[apdu_len], 0, data->subscriberProcessIdentifier);
|
||||
len =
|
||||
encode_context_unsigned(apdu, 0, data->subscriberProcessIdentifier);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 1 - initiatingDeviceIdentifier */
|
||||
len = encode_context_object_id(&apdu[apdu_len], 1, OBJECT_DEVICE,
|
||||
data->initiatingDeviceIdentifier);
|
||||
len = encode_context_object_id(
|
||||
apdu, 1, OBJECT_DEVICE, data->initiatingDeviceIdentifier);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 2 - monitoredObjectIdentifier */
|
||||
len = encode_context_object_id(&apdu[apdu_len], 2,
|
||||
len = encode_context_object_id(apdu, 2,
|
||||
data->monitoredObjectIdentifier.type,
|
||||
data->monitoredObjectIdentifier.instance);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 3 - timeRemaining */
|
||||
len = encode_context_unsigned(&apdu[apdu_len], 3, data->timeRemaining);
|
||||
len = encode_context_unsigned(apdu, 3, data->timeRemaining);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* tag 4 - listOfValues */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 4);
|
||||
len = encode_opening_tag(apdu, 4);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* the first value includes a pointer to the next value, etc */
|
||||
/* FIXME: for small implementations, we might try a partial
|
||||
approach like the rpm.c where the values are encoded with
|
||||
a separate function */
|
||||
value = data->listOfValues;
|
||||
while (value != NULL) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
len = encode_context_enumerated(
|
||||
&apdu[apdu_len], 0, value->propertyIdentifier);
|
||||
len = bacapp_property_value_encode(apdu, value);
|
||||
apdu_len += len;
|
||||
/* tag 1 - propertyArrayIndex OPTIONAL */
|
||||
if (value->propertyArrayIndex != BACNET_ARRAY_ALL) {
|
||||
len = encode_context_unsigned(
|
||||
&apdu[apdu_len], 1, value->propertyArrayIndex);
|
||||
apdu_len += len;
|
||||
}
|
||||
/* tag 2 - value */
|
||||
/* abstract syntax gets enclosed in a context tag */
|
||||
len = encode_opening_tag(&apdu[apdu_len], 2);
|
||||
apdu_len += len;
|
||||
app_data = &value->value;
|
||||
while (app_data != NULL) {
|
||||
len = bacapp_encode_application_data(&apdu[apdu_len], app_data);
|
||||
apdu_len += len;
|
||||
app_data = app_data->next;
|
||||
}
|
||||
|
||||
len = encode_closing_tag(&apdu[apdu_len], 2);
|
||||
apdu_len += len;
|
||||
/* tag 3 - priority OPTIONAL */
|
||||
if (value->priority != BACNET_NO_PRIORITY) {
|
||||
len = encode_context_unsigned(
|
||||
&apdu[apdu_len], 3, value->priority);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* is there another one to encode? */
|
||||
/* FIXME: check to see if there is room in the APDU */
|
||||
value = value->next;
|
||||
}
|
||||
len = encode_closing_tag(&apdu[apdu_len], 4);
|
||||
len = encode_closing_tag(apdu, 4);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode APDU for notification.
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding into
|
||||
* @param apdu_size number of bytes available in the buffer
|
||||
* @param data Pointer to the data to encode.
|
||||
*
|
||||
* @return bytes encoded or zero if unable to encode
|
||||
*/
|
||||
static int notify_encode_apdu(
|
||||
uint8_t *apdu, unsigned apdu_size, BACNET_COV_DATA *data)
|
||||
{
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
apdu_len = cov_notify_encode_apdu(NULL, data);
|
||||
if (apdu_len > apdu_size) {
|
||||
apdu_len = 0;
|
||||
} else {
|
||||
apdu_len = cov_notify_encode_apdu(apdu, data);
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode APDU for confirmed notification.
|
||||
*
|
||||
@@ -158,7 +164,7 @@ int ccov_notify_encode_apdu(uint8_t *apdu,
|
||||
apdu_len = 4;
|
||||
len =
|
||||
notify_encode_apdu(&apdu[apdu_len], max_apdu_len - apdu_len, data);
|
||||
if (len < 0) {
|
||||
if (len <= 0) {
|
||||
/* return the error */
|
||||
apdu_len = len;
|
||||
} else {
|
||||
@@ -190,7 +196,7 @@ int ucov_notify_encode_apdu(
|
||||
apdu_len = 2;
|
||||
len =
|
||||
notify_encode_apdu(&apdu[apdu_len], max_apdu_len - apdu_len, data);
|
||||
if (len < 0) {
|
||||
if (len <= 0) {
|
||||
/* return the error */
|
||||
apdu_len = len;
|
||||
} else {
|
||||
@@ -202,164 +208,120 @@ int ucov_notify_encode_apdu(
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the COV-service request only.
|
||||
* Note: COV and Unconfirmed COV are the same.
|
||||
* @brief Decode the COV-service request only.
|
||||
*
|
||||
* ConfirmedCOVNotification-Request ::= SEQUENCE {
|
||||
* subscriber-process-identifier [0] Unsigned32,
|
||||
* initiating-device-identifier [1] BACnetObjectIdentifier,
|
||||
* monitored-object-identifier [2] BACnetObjectIdentifier,
|
||||
* time-remaining [3] Unsigned,
|
||||
* list-of-values [4] SEQUENCE OF BACnetPropertyValue
|
||||
* }
|
||||
*
|
||||
* @note: COV and Unconfirmed COV are the same.
|
||||
* @param apdu Pointer to the buffer.
|
||||
* @param apdu_len Count of valid bytes in the buffer.
|
||||
* @param data Pointer to the data to store the decoded values.
|
||||
* @param apdu_size Number of valid bytes in the buffer.
|
||||
* @param data Pointer to the data to store the decoded values, or NULL
|
||||
*
|
||||
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
|
||||
* @return Bytes decoded or BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int cov_notify_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_COV_DATA *data)
|
||||
uint8_t *apdu, unsigned apdu_size, BACNET_COV_DATA *data)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
int app_len = 0;
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
BACNET_UNSIGNED_INTEGER decoded_value = 0; /* for decoding */
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE; /* for decoding */
|
||||
uint32_t property = 0; /* for decoding */
|
||||
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
|
||||
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
|
||||
int value_len = 0, tag_len = 0;
|
||||
BACNET_UNSIGNED_INTEGER decoded_value = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
|
||||
uint32_t decoded_instance = 0;
|
||||
BACNET_PROPERTY_ID property_identifier = PROP_ALL;
|
||||
BACNET_PROPERTY_VALUE *value = NULL;
|
||||
|
||||
if ((apdu_len > 2) && data) {
|
||||
/* tag 0 - subscriberProcessIdentifier */
|
||||
if (decode_is_context_tag(&apdu[len], 0)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
/* subscriber-process-identifier [0] Unsigned32 */
|
||||
value_len = bacnet_unsigned_context_decode(
|
||||
&apdu[len], apdu_size - len, 0, &decoded_value);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->subscriberProcessIdentifier = decoded_value;
|
||||
} else {
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* initiating-device-identifier [1] BACnetObjectIdentifier */
|
||||
value_len = bacnet_object_id_context_decode(
|
||||
&apdu[len], apdu_size - len, 1, &decoded_type, &decoded_instance);
|
||||
if (value_len > 0) {
|
||||
if (decoded_type != OBJECT_DEVICE) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* tag 1 - initiatingDeviceIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
if (data) {
|
||||
data->initiatingDeviceIdentifier = decoded_instance;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 1)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_object_id(
|
||||
&apdu[len], &decoded_type, &data->initiatingDeviceIdentifier);
|
||||
if (decoded_type != OBJECT_DEVICE) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* tag 2 - monitoredObjectIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 2)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_object_id(&apdu[len], &decoded_type,
|
||||
&data->monitoredObjectIdentifier.instance);
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* monitored-object-identifier [2] BACnetObjectIdentifier */
|
||||
value_len = bacnet_object_id_context_decode(
|
||||
&apdu[len], apdu_size - len, 2, &decoded_type, &decoded_instance);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->monitoredObjectIdentifier.type = decoded_type;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
data->monitoredObjectIdentifier.instance = decoded_instance;
|
||||
}
|
||||
/* tag 3 - timeRemaining */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 3)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* time-remaining [3] Unsigned */
|
||||
value_len = bacnet_unsigned_context_decode(
|
||||
&apdu[len], apdu_size - len, 3, &decoded_value);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->timeRemaining = decoded_value;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* list-of-values [4] SEQUENCE OF BACnetPropertyValue */
|
||||
if (bacnet_is_opening_tag_number(
|
||||
&apdu[len], apdu_size - len, 4, &tag_len)) {
|
||||
if (data) {
|
||||
len += tag_len;
|
||||
/* the first value includes a pointer to the next value, etc */
|
||||
value = data->listOfValues;
|
||||
while (value != NULL) {
|
||||
value_len = bacapp_property_value_decode(
|
||||
&apdu[len], apdu_size - len, value);
|
||||
if (value_len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
len += value_len;
|
||||
}
|
||||
/* end of list? */
|
||||
if (bacnet_is_closing_tag_number(
|
||||
&apdu[len], apdu_size - len, 4, &tag_len)) {
|
||||
len += tag_len;
|
||||
value->next = NULL;
|
||||
break;
|
||||
}
|
||||
/* is there another one to decode? */
|
||||
value = value->next;
|
||||
if (value == NULL) {
|
||||
/* out of room to store next value */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* tag 4: opening context tag - listOfValues */
|
||||
if (!decode_is_opening_tag_number(&apdu[len], 4)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* a tag number of 4 is not extended so only one octet */
|
||||
len++;
|
||||
/* the first value includes a pointer to the next value, etc */
|
||||
value = data->listOfValues;
|
||||
if (value == NULL) {
|
||||
/* no space to store any values */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
while (value != NULL) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 0)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_enumerated(&apdu[len], len_value, &property);
|
||||
value->propertyIdentifier = (BACNET_PROPERTY_ID)property;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* tag 1 - propertyArrayIndex OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 1)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
value->propertyArrayIndex = decoded_value;
|
||||
} else {
|
||||
value->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* tag 2: opening context tag - value */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
app_data = &value->value;
|
||||
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
|
||||
if (app_data == NULL) {
|
||||
/* out of room to store more values */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
app_len = bacapp_decode_application_data(
|
||||
&apdu[len], apdu_len - len, app_data);
|
||||
if (app_len < 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
len += app_len;
|
||||
|
||||
app_data = app_data->next;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
/* tag 3 - priority OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 3)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
value->priority = (uint8_t)decoded_value;
|
||||
} else {
|
||||
value->priority = BACNET_NO_PRIORITY;
|
||||
}
|
||||
/* end of list? */
|
||||
if (decode_is_closing_tag_number(&apdu[len], 4)) {
|
||||
value->next = NULL;
|
||||
break;
|
||||
}
|
||||
/* is there another one to decode? */
|
||||
value = value->next;
|
||||
if (value == NULL) {
|
||||
/* out of room to store more values */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* this len function needs to start at the opening tag
|
||||
to match opening/closing tags like a stack.
|
||||
However, it returns the len between the tags. */
|
||||
value_len = bacapp_data_len(&apdu[len], apdu_size - len,
|
||||
(BACNET_PROPERTY_ID)property_identifier);
|
||||
len += value_len;
|
||||
/* add the opening tag length to the totals */
|
||||
len += tag_len;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,79 +404,112 @@ int cov_subscribe_encode_apdu(uint8_t *apdu,
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the subscribe-service request only.
|
||||
* @brief Decode the subscribe-service request only.
|
||||
*
|
||||
* SubscribeCOV-Request ::= SEQUENCE {
|
||||
* subscriberProcessIdentifier [0] Unsigned32,
|
||||
* monitoredObjectIdentifier [1] BACnetObjectIdentifier,
|
||||
* issueConfirmedNotifications [2] BOOLEAN OPTIONAL,
|
||||
* lifetime [3] Unsigned OPTIONAL
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer.
|
||||
* @param apdu_len Count of valid bytes in the buffer.
|
||||
* @param apdu_size number of valid bytes in the buffer.
|
||||
* @param data Pointer to the data to store the decoded values.
|
||||
*
|
||||
* @return Bytes decoded or Zero/BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
int cov_subscribe_decode_service_request(
|
||||
uint8_t *apdu, unsigned apdu_len, BACNET_SUBSCRIBE_COV_DATA *data)
|
||||
uint8_t *apdu, unsigned apdu_size, BACNET_SUBSCRIBE_COV_DATA *data)
|
||||
{
|
||||
int len = 0; /* return value */
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
BACNET_UNSIGNED_INTEGER unsigned_value = 0;
|
||||
int value_len = 0;
|
||||
BACNET_UNSIGNED_INTEGER decoded_value = 0;
|
||||
BACNET_OBJECT_TYPE decoded_type = OBJECT_NONE;
|
||||
uint32_t decoded_instance = 0;
|
||||
bool decoded_boolean = false;
|
||||
|
||||
if ((apdu_len > 2) && data) {
|
||||
/* tag 0 - subscriberProcessIdentifier */
|
||||
if (decode_is_context_tag(&apdu[len], 0)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &unsigned_value);
|
||||
data->subscriberProcessIdentifier = unsigned_value;
|
||||
} else {
|
||||
/* subscriberProcessIdentifier [0] Unsigned32 */
|
||||
value_len = bacnet_unsigned_context_decode(
|
||||
&apdu[len], apdu_size - len, 0, &decoded_value);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->subscriberProcessIdentifier = decoded_value;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* tag 1 - monitoredObjectIdentifier */
|
||||
if ((unsigned)len >= apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 1)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_object_id(&apdu[len], &decoded_type,
|
||||
&data->monitoredObjectIdentifier.instance);
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* monitoredObjectIdentifier [1] BACnetObjectIdentifier */
|
||||
value_len = bacnet_object_id_context_decode(
|
||||
&apdu[len], apdu_size - len, 1, &decoded_type, &decoded_instance);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->monitoredObjectIdentifier.type = decoded_type;
|
||||
} else {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
return BACNET_STATUS_REJECT;
|
||||
data->monitoredObjectIdentifier.instance = decoded_instance;
|
||||
}
|
||||
/* optional parameters - if missing, means cancellation */
|
||||
if ((unsigned)len < apdu_len) {
|
||||
/* tag 2 - issueConfirmedNotifications - optional */
|
||||
if (decode_is_context_tag(&apdu[len], 2)) {
|
||||
data->cancellationRequest = false;
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
data->issueConfirmedNotifications =
|
||||
decode_context_boolean(&apdu[len]);
|
||||
len += len_value;
|
||||
} else {
|
||||
data->cancellationRequest = true;
|
||||
len += value_len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if ((unsigned)len < apdu_size) {
|
||||
if (data) {
|
||||
/* does not indicate a cancellation request */
|
||||
data->cancellationRequest = false;
|
||||
}
|
||||
/* issueConfirmedNotifications [2] BOOLEAN OPTIONAL */
|
||||
value_len = bacnet_boolean_context_decode(
|
||||
&apdu[len], apdu_size - len, 2, &decoded_boolean);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->issueConfirmedNotifications = decoded_boolean;
|
||||
}
|
||||
/* tag 3 - lifetime - optional */
|
||||
if ((unsigned)len < apdu_len) {
|
||||
if (decode_is_context_tag(&apdu[len], 3)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len +=
|
||||
decode_unsigned(&apdu[len], len_value, &unsigned_value);
|
||||
data->lifetime = unsigned_value;
|
||||
} else {
|
||||
data->lifetime = 0;
|
||||
}
|
||||
} else {
|
||||
data->lifetime = 0;
|
||||
len += value_len;
|
||||
} else if (value_len == 0) {
|
||||
/* invalid tag */
|
||||
if (data) {
|
||||
data->issueConfirmedNotifications = false;
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
/* If both the 'Issue Confirmed Notifications' and
|
||||
'Lifetime' parameters are absent, then this shall
|
||||
indicate a cancellation request. */
|
||||
if (data) {
|
||||
data->cancellationRequest = true;
|
||||
}
|
||||
}
|
||||
if ((unsigned)len < apdu_size) {
|
||||
/* lifetime [3] Unsigned OPTIONAL */
|
||||
value_len = bacnet_unsigned_context_decode(
|
||||
&apdu[len], apdu_size - len, 3, &decoded_value);
|
||||
if (value_len > 0) {
|
||||
if (data) {
|
||||
data->lifetime = decoded_value;
|
||||
}
|
||||
len += value_len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (data) {
|
||||
data->lifetime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ typedef struct BACnet_COV_Notification {
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int cov_notify_encode_apdu(
|
||||
uint8_t *apdu, BACNET_COV_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int ucov_notify_encode_apdu(
|
||||
uint8_t * apdu,
|
||||
unsigned max_apdu_len,
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief CreateObject service encode and decode
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/create_object.h"
|
||||
|
||||
/**
|
||||
* @brief Encode the CreateObject service request
|
||||
*
|
||||
* CreateObject-Request ::= SEQUENCE {
|
||||
* object-specifier [0] CHOICE {
|
||||
* object-type [0] BACnetObjectType,
|
||||
* object-identifier [1] BACnetObjectIdentifier
|
||||
* },
|
||||
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoded values
|
||||
* @param data Pointer to the service data used for encoding values
|
||||
*
|
||||
* @return Bytes encoded or zero on error.
|
||||
*/
|
||||
int create_object_encode_service_request(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
BACNET_PROPERTY_VALUE *value = NULL; /* value in list */
|
||||
|
||||
if (data) {
|
||||
/* object-specifier [0] */
|
||||
len = encode_opening_tag(apdu, 0);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
if (data->object_instance >= BACNET_MAX_INSTANCE) {
|
||||
/* object-type [0] BACnetObjectType */
|
||||
len = encode_context_enumerated(apdu, 0, data->object_type);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
} else {
|
||||
/* object-identifier [1] BACnetObjectIdentifier */
|
||||
len = encode_context_object_id(
|
||||
apdu, 1, data->object_type, data->object_instance);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
}
|
||||
len = encode_closing_tag(apdu, 0);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
if (data->list_of_initial_values) {
|
||||
/* list-of-initial-values [1] OPTIONAL */
|
||||
len = encode_opening_tag(apdu, 1);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* the first value includes a pointer to the next value, etc */
|
||||
value = data->list_of_initial_values;
|
||||
while (value != NULL) {
|
||||
/* SEQUENCE OF BACnetPropertyValue */
|
||||
len = bacapp_property_value_encode(apdu, value);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
/* is there another one to encode? */
|
||||
/* FIXME: check to see if there is room in the APDU */
|
||||
value = value->next;
|
||||
}
|
||||
len = encode_closing_tag(apdu, 1);
|
||||
apdu_len += len;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the CreateObject service request
|
||||
*
|
||||
* CreateObject-Request ::= SEQUENCE {
|
||||
* object-specifier [0] CHOICE {
|
||||
* object-type [0] BACnetObjectType,
|
||||
* object-identifier [1] BACnetObjectIdentifier
|
||||
* },
|
||||
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for decoding.
|
||||
* @param apdu_len Count of valid bytes in the buffer.
|
||||
* @param data Pointer to the property decoded data to be stored
|
||||
*
|
||||
* @return Bytes decoded or BACNET_STATUS_REJECT on error.
|
||||
*/
|
||||
int create_object_decode_service_request(
|
||||
uint8_t *apdu, uint32_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
|
||||
uint32_t object_instance = 0;
|
||||
uint32_t enumerated_value = 0;
|
||||
BACNET_PROPERTY_VALUE *list_of_initial_values = NULL;
|
||||
|
||||
/* object-specifier [0] CHOICE */
|
||||
if (!bacnet_is_opening_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* CHOICE of Tag [0] or [1] */
|
||||
/* object-identifier [1] BACnetObjectIdentifier */
|
||||
len = bacnet_object_id_context_decode(&apdu[apdu_len], apdu_size - apdu_len,
|
||||
1, &object_type, &object_instance);
|
||||
if ((len != BACNET_STATUS_ERROR) && (len != 0)) {
|
||||
if ((object_type >= MAX_BACNET_OBJECT_TYPE) ||
|
||||
(object_instance >= BACNET_MAX_INSTANCE)) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (data) {
|
||||
data->object_instance = object_instance;
|
||||
data->object_type = object_type;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
/* object-type [0] BACnetObjectType */
|
||||
len = bacnet_enumerated_context_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value);
|
||||
if ((len != BACNET_STATUS_ERROR) && (len != 0)) {
|
||||
if (enumerated_value >= MAX_BACNET_OBJECT_TYPE) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (data) {
|
||||
data->object_instance = BACNET_MAX_INSTANCE;
|
||||
data->object_type = enumerated_value;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
}
|
||||
if (!bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
/* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL */
|
||||
if (bacnet_is_opening_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
|
||||
apdu_len += len;
|
||||
if (data) {
|
||||
list_of_initial_values = data->list_of_initial_values;
|
||||
}
|
||||
len = bacapp_property_value_decode(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, list_of_initial_values);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
if (!bacnet_is_closing_tag_number(
|
||||
&apdu[apdu_len], apdu_size - apdu_len, 0, &len)) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_INVALID_TAG;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a CreateObject-ACK APDU service data
|
||||
*
|
||||
* CreateObject-ACK ::= BACnetObjectIdentifier
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding, or NULL for length
|
||||
* @param data Pointer to the property data to be encoded.
|
||||
* @return number of bytes encoded
|
||||
*/
|
||||
int create_object_ack_service_encode(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
/* BACnetObjectIdentifier */
|
||||
return encode_application_object_id(
|
||||
apdu, data->object_type, data->object_instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a CreateObject-ACK APDU
|
||||
*
|
||||
* CreateObject-ACK ::= BACnetObjectIdentifier
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding, or NULL for length
|
||||
* @param invoke_id original invoke id from request
|
||||
* @param data Pointer to the property data to be encoded.
|
||||
* @return number of bytes encoded
|
||||
*/
|
||||
int create_object_ack_encode(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int apdu_len = 3; /* total length of the apdu, return value */
|
||||
|
||||
if (apdu) {
|
||||
/* service */
|
||||
apdu[0] = PDU_TYPE_COMPLEX_ACK;
|
||||
/* original invoke id from request */
|
||||
apdu[1] = invoke_id;
|
||||
/* service choice */
|
||||
apdu[2] = SERVICE_CONFIRMED_CREATE_OBJECT;
|
||||
apdu += apdu_len;
|
||||
}
|
||||
apdu_len += create_object_ack_service_encode(apdu, data);
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decoding for CreateObject-ACK APDU service data
|
||||
* CreateObject-ACK ::= BACnetObjectIdentifier
|
||||
*
|
||||
* @param apdu Pointer to the buffer for decoding.
|
||||
* @param apdu_size size of the buffer for decoding.
|
||||
* @param data Pointer to the property data to be encoded.
|
||||
* @return Bytes encoded or BACNET_STATUS_REJECT on error.
|
||||
*/
|
||||
int create_object_ack_service_decode(
|
||||
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int apdu_len = 0;
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
|
||||
uint32_t object_instance = 0;
|
||||
|
||||
apdu_len = bacnet_object_id_application_decode(
|
||||
apdu, apdu_size, &object_type, &object_instance);
|
||||
if (apdu_len > 0) {
|
||||
if (data) {
|
||||
data->object_instance = object_instance;
|
||||
data->object_type = object_type;
|
||||
}
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode a CreateObject-Error ACK APDU
|
||||
*
|
||||
* CreateObject-Error ::= SEQUENCE {
|
||||
* error-type [0] Error,
|
||||
* first-failed-element-number [1] Unsigned
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoding.
|
||||
* @param data Pointer to the property data to be encoded.
|
||||
* @return Bytes encoded or zero on error.
|
||||
*/
|
||||
int create_object_error_ack_service_encode(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
len = encode_opening_tag(apdu, 0);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_application_enumerated(apdu, data->error_class);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_application_enumerated(apdu, data->error_code);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_closing_tag(apdu, 0);
|
||||
apdu_len += len;
|
||||
if (apdu) {
|
||||
apdu += len;
|
||||
}
|
||||
len = encode_context_unsigned(apdu, 1, data->first_failed_element_number);
|
||||
apdu_len += len;
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Encode an Error acknowledge in the APDU.
|
||||
* @param apdu [in] The APDU buffer.
|
||||
* @param invoke_id [in] Invoked service ID.
|
||||
* @param data [in] Data of the invoked property.
|
||||
* @return number of bytes encoded
|
||||
*/
|
||||
int create_object_error_ack_encode(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 3;
|
||||
|
||||
if (apdu) {
|
||||
apdu[0] = PDU_TYPE_ERROR;
|
||||
apdu[1] = invoke_id;
|
||||
apdu[2] = SERVICE_CONFIRMED_CREATE_OBJECT;
|
||||
apdu += len;
|
||||
}
|
||||
len += create_object_error_ack_service_encode(apdu, data);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode a CreateObject-Error ACK APDU
|
||||
*
|
||||
* CreateObject-Error ::= SEQUENCE {
|
||||
* error-type [0] Error,
|
||||
* first-failed-element-number [1] Unsigned
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for decoding.
|
||||
* @param apdu_size size of the buffer for decoding.
|
||||
* @param data Pointer to the property data to be encoded.
|
||||
* @return Bytes encoded or BACNET_STATUS_REJECT on error.
|
||||
*/
|
||||
int create_object_error_ack_service_decode(
|
||||
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0, apdu_len = 0;
|
||||
BACNET_ERROR_CLASS error_class = ERROR_CLASS_SERVICES;
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_SUCCESS;
|
||||
BACNET_UNSIGNED_INTEGER first_failed_element_number = 0;
|
||||
|
||||
if (!apdu) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (data) {
|
||||
data->first_failed_element_number = 0;
|
||||
data->error_class = ERROR_CLASS_SERVICES;
|
||||
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
|
||||
}
|
||||
if (apdu_size < apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* Opening Context tag 0 - Error */
|
||||
if (decode_is_opening_tag_number(apdu, 0)) {
|
||||
/* opening tag 0 is 1 byte */
|
||||
len = 1;
|
||||
apdu_len += len;
|
||||
apdu += len;
|
||||
} else {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (apdu_size < apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = bacerror_decode_error_class_and_code(
|
||||
apdu, apdu_size - apdu_len, &error_class, &error_code);
|
||||
if (len > 0) {
|
||||
if (data) {
|
||||
data->error_class = error_class;
|
||||
data->error_code = error_code;
|
||||
}
|
||||
apdu_len += len;
|
||||
apdu += len;
|
||||
} else {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (apdu_size < apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
/* Closing Context tag 0 - Error */
|
||||
if (decode_is_closing_tag_number(apdu, 0)) {
|
||||
/* closing tag 0 is 1 byte */
|
||||
len = 1;
|
||||
apdu_len += len;
|
||||
apdu += len;
|
||||
} else {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (apdu_size < apdu_len) {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
len = bacnet_unsigned_context_decode(
|
||||
apdu, apdu_size - apdu_len, 1, &first_failed_element_number);
|
||||
if (len > 0) {
|
||||
if (data) {
|
||||
data->first_failed_element_number = first_failed_element_number;
|
||||
}
|
||||
apdu_len += len;
|
||||
} else {
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for CreateObject service encode and decode
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_CREATE_OBJECT_H
|
||||
#define BACNET_CREATE_OBJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
|
||||
/**
|
||||
* CreateObject-Request ::= SEQUENCE {
|
||||
* object-specifier [0] CHOICE {
|
||||
* object-type [0] BACnetObjectType,
|
||||
* object-identifier [1] BACnetObjectIdentifier
|
||||
* },
|
||||
* list-of-initial-values [1] SEQUENCE OF BACnetPropertyValue OPTIONAL
|
||||
* }
|
||||
*/
|
||||
typedef struct BACnet_Create_Object_Data {
|
||||
/* note: use BACNET_MAX_INSTANCE to choose CHOICE=[0] object_type */
|
||||
uint32_t object_instance;
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
/* simple linked list of values */
|
||||
BACNET_PROPERTY_VALUE *list_of_initial_values;
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
BACNET_UNSIGNED_INTEGER first_failed_element_number;
|
||||
} BACNET_CREATE_OBJECT_DATA;
|
||||
|
||||
/**
|
||||
* @brief CreateObject service handler for an object
|
||||
* @ingroup ObjHelpers
|
||||
* @param object_instance [in] instance number of the object to create,
|
||||
* or BACNET_MAX_INSTANCE to create the next free object instance
|
||||
* @return object instance number created, or BACNET_MAX_INSTANCE if not
|
||||
*/
|
||||
typedef uint32_t (*create_object_function)(uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_encode_service_request(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_encode_service_ack_encode(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_decode_service_request(
|
||||
uint8_t *apdu, uint32_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_ack_service_encode(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_ack_service_decode(
|
||||
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_ack_encode(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_error_ack_service_encode(
|
||||
uint8_t *apdu, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_error_ack_service_decode(
|
||||
uint8_t *apdu, uint16_t apdu_size, BACNET_CREATE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_error_ack_encode(
|
||||
uint8_t *apdu, uint8_t invoke_id, BACNET_CREATE_OBJECT_DATA *data);
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int create_object_ack_encode(
|
||||
uint8_t *apdu, uint8_t invoke_id,
|
||||
BACNET_CREATE_OBJECT_DATA *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,92 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief DeleteObject service encode and decode
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacerror.h"
|
||||
#include "bacnet/delete_object.h"
|
||||
|
||||
/**
|
||||
* @brief Encode the DeleteObject service request
|
||||
*
|
||||
* DeleteObject-Request ::= SEQUENCE {
|
||||
* object-identifier BACnetObjectIdentifier
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for encoded values, or NULL for length
|
||||
* @param data Pointer to the service data used for encoding values
|
||||
*
|
||||
* @return Bytes encoded or zero on error.
|
||||
*/
|
||||
int delete_object_encode_service_request(
|
||||
uint8_t *apdu, BACNET_DELETE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0; /* length of each encoding */
|
||||
int apdu_len = 0; /* total length of the apdu, return value */
|
||||
|
||||
if (data) {
|
||||
/* object-identifier BACnetObjectIdentifier */
|
||||
len = encode_application_object_id(
|
||||
apdu, data->object_type, data->object_instance);
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode the DeleteObject service request
|
||||
*
|
||||
* DeleteObject-Request ::= SEQUENCE {
|
||||
* object-identifier BACnetObjectIdentifier
|
||||
* }
|
||||
*
|
||||
* @param apdu Pointer to the buffer for decoding.
|
||||
* @param apdu_size Count of valid bytes in the buffer.
|
||||
* @param data Pointer to the property decoded data to be stored
|
||||
*
|
||||
* @return Bytes decoded or BACNET_STATUS_REJECT on error.
|
||||
*/
|
||||
int delete_object_decode_service_request(
|
||||
uint8_t *apdu, uint32_t apdu_size, BACNET_DELETE_OBJECT_DATA *data)
|
||||
{
|
||||
int len = 0;
|
||||
int apdu_len = 0;
|
||||
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
|
||||
uint32_t object_instance = 0;
|
||||
|
||||
/* object-identifier BACnetObjectIdentifier */
|
||||
len = bacnet_object_id_application_decode(&apdu[apdu_len],
|
||||
apdu_size - apdu_len, &object_type, &object_instance);
|
||||
if (len == BACNET_STATUS_ERROR) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
} else {
|
||||
if ((object_type >= MAX_BACNET_OBJECT_TYPE) ||
|
||||
(object_instance > BACNET_MAX_INSTANCE)) {
|
||||
if (data) {
|
||||
data->error_code = ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE;
|
||||
}
|
||||
return BACNET_STATUS_REJECT;
|
||||
}
|
||||
if (data) {
|
||||
data->object_instance = object_instance;
|
||||
data->object_type = object_type;
|
||||
}
|
||||
apdu_len += len;
|
||||
}
|
||||
|
||||
return apdu_len;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief API for DeleteObject service encode and decode
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date August 2023
|
||||
* @section LICENSE
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#ifndef BACNET_DELETE_OBJECT_H
|
||||
#define BACNET_DELETE_OBJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "bacnet/bacnet_stack_exports.h"
|
||||
#include "bacnet/bacenum.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
|
||||
/**
|
||||
* DeleteObject-Request ::= SEQUENCE {
|
||||
* object-identifier BACnetObjectIdentifier
|
||||
*/
|
||||
typedef struct BACnet_Delete_Object_Data {
|
||||
uint32_t object_instance;
|
||||
BACNET_OBJECT_TYPE object_type;
|
||||
/* application layer stores specific abort/reject/error */
|
||||
BACNET_ERROR_CLASS error_class;
|
||||
BACNET_ERROR_CODE error_code;
|
||||
} BACNET_DELETE_OBJECT_DATA;
|
||||
|
||||
/**
|
||||
* @brief DeleteObject service handler for an object
|
||||
* @ingroup ObjHelpers
|
||||
* @param object_instance [in] instance number of the object to delete
|
||||
* @return true if the object instance is deleted
|
||||
*/
|
||||
typedef bool (*delete_object_function)(uint32_t object_instance);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
BACNET_STACK_EXPORT
|
||||
int delete_object_encode_service_request(
|
||||
uint8_t *apdu, BACNET_DELETE_OBJECT_DATA *data);
|
||||
BACNET_STACK_EXPORT
|
||||
int delete_object_decode_service_request(
|
||||
uint8_t *apdu, uint32_t apdu_size, BACNET_DELETE_OBJECT_DATA *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif
|
||||
+106
-216
@@ -41,178 +41,52 @@
|
||||
/** @file event.c Encode/Decode Event Notifications */
|
||||
|
||||
/**
|
||||
* Parse the array of complex-event-type notification parameters
|
||||
*
|
||||
* @param len - parse position inside APDU
|
||||
* @param apdu_len - APDU total length
|
||||
* @brief Decode the array of complex-event-type notification parameters
|
||||
* @param apdu - apdu buffer
|
||||
* @param apdu_size - APDU total length
|
||||
* @param data - the event data struct to store the results in
|
||||
* @return new value of "len", or negative on error
|
||||
* @return number of apdu bytes decoded, or BACNET_STATUS_ERROR on error.
|
||||
*/
|
||||
static int parse_complex_event_type_values(int len, unsigned apdu_len, uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data)
|
||||
static int complex_event_type_values_decode(
|
||||
uint8_t *apdu, unsigned apdu_size, BACNET_EVENT_NOTIFICATION_DATA *data)
|
||||
{
|
||||
/*
|
||||
BACnetPropertyValue ::= SEQUENCE {
|
||||
property-identifier [0] BACnetPropertyIdentifier,
|
||||
property-array-index [1] Unsigned OPTIONAL, -- used only with array datatypes
|
||||
-- if omitted with an array the entire array is referenced
|
||||
property-value [2] ABSTRACT-SYNTAX.&Type, -- any datatype appropriate for the specified property
|
||||
priority [3] Unsigned (1..16) OPTIONAL -- used only when property is commandable
|
||||
}
|
||||
*/
|
||||
|
||||
/* TODO this is mostly copied from "cov_notify_decode_service_request" - extract to a common function? */
|
||||
|
||||
uint8_t tag_number = 0;
|
||||
uint32_t len_value = 0;
|
||||
uint32_t property = 0;
|
||||
BACNET_UNSIGNED_INTEGER decoded_value = 0;
|
||||
BACNET_APPLICATION_DATA_VALUE *app_data = NULL;
|
||||
int app_len = 0;
|
||||
int len = 0; /* return value */
|
||||
BACNET_PROPERTY_VALUE *value;
|
||||
int value_len = 0, tag_len = 0;
|
||||
|
||||
#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1)
|
||||
BACNET_PROPERTY_VALUE *values;
|
||||
BACNET_PROPERTY_VALUE *value;
|
||||
/* we want to extract the values */
|
||||
values = data->notificationParams.complexEventType.values;
|
||||
bacapp_property_value_list_init(values, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS);
|
||||
value = values;
|
||||
for(;;) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 0)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_enumerated(&apdu[len], len_value, &property);
|
||||
value->propertyIdentifier = (BACNET_PROPERTY_ID)property;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
/* tag 1 - propertyArrayIndex OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 1)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
value->propertyArrayIndex = decoded_value;
|
||||
} else {
|
||||
value->propertyArrayIndex = BACNET_ARRAY_ALL;
|
||||
}
|
||||
/* tag 2: opening context tag - value */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
app_data = &value->value;
|
||||
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
|
||||
if (app_data == NULL) {
|
||||
/* out of room to store more values */
|
||||
return -1;
|
||||
}
|
||||
app_len = bacapp_decode_application_data(
|
||||
&apdu[len], apdu_len - len, app_data);
|
||||
if (app_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
len += app_len;
|
||||
|
||||
app_data = app_data->next;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
/* tag 3 - priority OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 3)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
value->priority = (uint8_t)decoded_value;
|
||||
} else {
|
||||
value->priority = BACNET_NO_PRIORITY;
|
||||
}
|
||||
/* end of list? */
|
||||
if (decode_is_closing_tag_number(&apdu[len], 6)) {
|
||||
break;
|
||||
}
|
||||
value = value->next;
|
||||
if (value == NULL) {
|
||||
/* out of room to store more values */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
value = data->notificationParams.complexEventType.values;
|
||||
bacapp_property_value_list_init(
|
||||
value, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS);
|
||||
#else
|
||||
/* we just want to discard the complex values */
|
||||
BACNET_PROPERTY_VALUE dummyValue;
|
||||
BACNET_PROPERTY_VALUE *value;
|
||||
|
||||
bacapp_property_value_list_init(&dummyValue, 1);
|
||||
value = &dummyValue;
|
||||
for(;;) {
|
||||
/* tag 0 - propertyIdentifier */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 0)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_enumerated(&apdu[len], len_value, &property);
|
||||
#endif
|
||||
while (value != NULL) {
|
||||
value_len =
|
||||
bacapp_property_value_decode(&apdu[len], apdu_size - len, value);
|
||||
if (value_len == BACNET_STATUS_ERROR) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
/* tag 1 - propertyArrayIndex OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 1)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
}
|
||||
/* tag 2: opening context tag - value */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (!decode_is_opening_tag_number(&apdu[len], 2)) {
|
||||
return -1;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
while (!decode_is_closing_tag_number(&apdu[len], 2)) {
|
||||
app_data = &value->value;
|
||||
app_len = bacapp_decode_application_data(
|
||||
&apdu[len], apdu_len - len, app_data);
|
||||
if (app_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
len += app_len;
|
||||
}
|
||||
/* a tag number of 2 is not extended so only one octet */
|
||||
len++;
|
||||
/* tag 3 - priority OPTIONAL */
|
||||
if (len >= (int)apdu_len) {
|
||||
return -1;
|
||||
}
|
||||
if (decode_is_context_tag(&apdu[len], 3)) {
|
||||
len += decode_tag_number_and_value(
|
||||
&apdu[len], &tag_number, &len_value);
|
||||
len += decode_unsigned(&apdu[len], len_value, &decoded_value);
|
||||
len += value_len;
|
||||
}
|
||||
/* end of list? */
|
||||
if (decode_is_closing_tag_number(&apdu[len], 6)) {
|
||||
if (bacnet_is_closing_tag_number(
|
||||
&apdu[len], apdu_size - len, 6, &tag_len)) {
|
||||
len += tag_len;
|
||||
value->next = NULL;
|
||||
break;
|
||||
}
|
||||
/* is there another one to decode? */
|
||||
value = value->next;
|
||||
if (value == NULL) {
|
||||
/* out of room to store next value */
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return len;
|
||||
}
|
||||
@@ -850,10 +724,10 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams
|
||||
.changeOfBitstring.statusFlags))) {
|
||||
(section_length = decode_context_bitstring(
|
||||
&apdu[len], 1,
|
||||
&data->notificationParams.changeOfBitstring
|
||||
.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -865,8 +739,8 @@ int event_notify_decode_service_request(
|
||||
(section_length =
|
||||
bacapp_decode_context_property_state(
|
||||
&apdu[len], 0,
|
||||
&data->notificationParams.changeOfState
|
||||
.newState))) {
|
||||
&data->notificationParams
|
||||
.changeOfState.newState))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -874,8 +748,8 @@ int event_notify_decode_service_request(
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.changeOfState
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.changeOfState.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -925,8 +799,8 @@ int event_notify_decode_service_request(
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.changeOfValue
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.changeOfValue.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -948,8 +822,9 @@ int event_notify_decode_service_request(
|
||||
switch (tag_number) {
|
||||
case BACNET_APPLICATION_TAG_ENUMERATED:
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value, &enum_value))) {
|
||||
(section_length =
|
||||
decode_enumerated(&apdu[len],
|
||||
len_value, &enum_value))) {
|
||||
return -1;
|
||||
}
|
||||
data->notificationParams.commandFailure
|
||||
@@ -978,10 +853,10 @@ int event_notify_decode_service_request(
|
||||
len++;
|
||||
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.commandFailure
|
||||
.statusFlags))) {
|
||||
(section_length = decode_context_bitstring(
|
||||
&apdu[len], 1,
|
||||
&data->notificationParams.commandFailure
|
||||
.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1001,8 +876,9 @@ int event_notify_decode_service_request(
|
||||
switch (tag_number) {
|
||||
case BACNET_APPLICATION_TAG_ENUMERATED:
|
||||
if (-1 ==
|
||||
(section_length = decode_enumerated(
|
||||
&apdu[len], len_value, &enum_value))) {
|
||||
(section_length =
|
||||
decode_enumerated(&apdu[len],
|
||||
len_value, &enum_value))) {
|
||||
return -1;
|
||||
}
|
||||
data->notificationParams.commandFailure
|
||||
@@ -1034,7 +910,8 @@ int event_notify_decode_service_request(
|
||||
|
||||
case EVENT_FLOATING_LIMIT:
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 0,
|
||||
(section_length = decode_context_real(
|
||||
&apdu[len], 0,
|
||||
&data->notificationParams.floatingLimit
|
||||
.referenceValue))) {
|
||||
return -1;
|
||||
@@ -1044,13 +921,14 @@ int event_notify_decode_service_request(
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.floatingLimit
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.floatingLimit.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 2,
|
||||
(section_length = decode_context_real(
|
||||
&apdu[len], 2,
|
||||
&data->notificationParams.floatingLimit
|
||||
.setPointValue))) {
|
||||
return -1;
|
||||
@@ -1058,9 +936,10 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 3,
|
||||
&data->notificationParams.floatingLimit
|
||||
.errorLimit))) {
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 3,
|
||||
&data->notificationParams
|
||||
.floatingLimit.errorLimit))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1068,9 +947,10 @@ int event_notify_decode_service_request(
|
||||
|
||||
case EVENT_OUT_OF_RANGE:
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 0,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceedingValue))) {
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 0,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceedingValue))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1084,17 +964,19 @@ int event_notify_decode_service_request(
|
||||
}
|
||||
len += section_length;
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 2,
|
||||
&data->notificationParams.outOfRange
|
||||
.deadband))) {
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 2,
|
||||
&data->notificationParams.outOfRange
|
||||
.deadband))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = decode_context_real(&apdu[len], 3,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceededLimit))) {
|
||||
(section_length =
|
||||
decode_context_real(&apdu[len], 3,
|
||||
&data->notificationParams.outOfRange
|
||||
.exceededLimit))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1106,7 +988,8 @@ int event_notify_decode_service_request(
|
||||
&apdu[len], 0, &enum_value))) {
|
||||
return -1;
|
||||
}
|
||||
data->notificationParams.changeOfLifeSafety.newState =
|
||||
data->notificationParams.changeOfLifeSafety
|
||||
.newState =
|
||||
(BACNET_LIFE_SAFETY_STATE)enum_value;
|
||||
len += section_length;
|
||||
|
||||
@@ -1115,15 +998,15 @@ int event_notify_decode_service_request(
|
||||
&apdu[len], 1, &enum_value))) {
|
||||
return -1;
|
||||
}
|
||||
data->notificationParams.changeOfLifeSafety.newMode =
|
||||
(BACNET_LIFE_SAFETY_MODE)enum_value;
|
||||
data->notificationParams.changeOfLifeSafety
|
||||
.newMode = (BACNET_LIFE_SAFETY_MODE)enum_value;
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = decode_context_bitstring(
|
||||
&apdu[len], 2,
|
||||
&data->notificationParams.changeOfLifeSafety
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.changeOfLifeSafety.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1145,7 +1028,8 @@ int event_notify_decode_service_request(
|
||||
(section_length =
|
||||
bacapp_decode_context_device_obj_property_ref(
|
||||
&apdu[len], 0,
|
||||
&data->notificationParams.bufferReady
|
||||
&data->notificationParams
|
||||
.bufferReady
|
||||
.bufferProperty))) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1190,7 +1074,8 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
if (unsigned_value <= UINT32_MAX) {
|
||||
data->notificationParams.unsignedRange
|
||||
.exceedingValue = (uint32_t)unsigned_value;
|
||||
.exceedingValue =
|
||||
(uint32_t)unsigned_value;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
@@ -1201,8 +1086,8 @@ int event_notify_decode_service_request(
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.unsignedRange
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.unsignedRange.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1213,7 +1098,8 @@ int event_notify_decode_service_request(
|
||||
len += section_length;
|
||||
if (unsigned_value <= UINT32_MAX) {
|
||||
data->notificationParams.unsignedRange
|
||||
.exceededLimit = (uint32_t)unsigned_value;
|
||||
.exceededLimit =
|
||||
(uint32_t)unsigned_value;
|
||||
} else {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
@@ -1234,26 +1120,28 @@ int event_notify_decode_service_request(
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_bitstring(&apdu[len], 1,
|
||||
&data->notificationParams.accessEvent
|
||||
.statusFlags))) {
|
||||
&data->notificationParams
|
||||
.accessEvent.statusFlags))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = decode_context_unsigned(
|
||||
&apdu[len], 2,
|
||||
&data->notificationParams.accessEvent
|
||||
.accessEventTag))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length =
|
||||
decode_context_unsigned(&apdu[len], 2,
|
||||
&data->notificationParams.accessEvent
|
||||
.accessEventTag))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
|
||||
if (-1 ==
|
||||
(section_length = bacapp_decode_context_timestamp(
|
||||
&apdu[len], 3,
|
||||
&data->notificationParams.accessEvent
|
||||
.accessEventTime))) {
|
||||
bacapp_decode_context_timestamp(
|
||||
&apdu[len], 3,
|
||||
&data->notificationParams
|
||||
.accessEvent
|
||||
.accessEventTime))) {
|
||||
return -1;
|
||||
}
|
||||
len += section_length;
|
||||
@@ -1262,7 +1150,8 @@ int event_notify_decode_service_request(
|
||||
(section_length =
|
||||
bacapp_decode_context_device_obj_ref(
|
||||
&apdu[len], 4,
|
||||
&data->notificationParams.accessEvent
|
||||
&data->notificationParams
|
||||
.accessEvent
|
||||
.accessCredential))) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1290,7 +1179,8 @@ int event_notify_decode_service_request(
|
||||
len++;
|
||||
is_complex_event_type = true;
|
||||
|
||||
len = parse_complex_event_type_values(len, apdu_len, apdu, data);
|
||||
len = complex_event_type_values_decode(
|
||||
&apdu[len], apdu_len - len, data);
|
||||
if (len < 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -1298,8 +1188,8 @@ int event_notify_decode_service_request(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (decode_is_closing_tag_number(
|
||||
&apdu[len], is_complex_event_type ? 6 : (uint8_t)data->eventType)) {
|
||||
if (decode_is_closing_tag_number(&apdu[len],
|
||||
is_complex_event_type ? 6 : (uint8_t)data->eventType)) {
|
||||
len++;
|
||||
} else {
|
||||
return -1;
|
||||
|
||||
+40
-2
@@ -77,6 +77,9 @@ BACNET_REJECT_REASON reject_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE:
|
||||
reject_code = REJECT_REASON_UNRECOGNIZED_SERVICE;
|
||||
break;
|
||||
case ERROR_CODE_INVALID_DATA_ENCODING:
|
||||
reject_code = REJECT_REASON_INVALID_DATA_ENCODING;
|
||||
break;
|
||||
case ERROR_CODE_REJECT_PROPRIETARY:
|
||||
reject_code = REJECT_REASON_PROPRIETARY_FIRST;
|
||||
break;
|
||||
@@ -89,6 +92,38 @@ BACNET_REJECT_REASON reject_convert_error_code(BACNET_ERROR_CODE error_code)
|
||||
return (reject_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Determine if a BACnetErrorCode is a BACnetRejectReason
|
||||
* @param error_code #BACNET_ERROR_CODE enumeration
|
||||
* @return true if the BACnet Error Code is a BACnet abort reason
|
||||
*/
|
||||
bool reject_valid_error_code(BACNET_ERROR_CODE error_code)
|
||||
{
|
||||
bool status = false;
|
||||
|
||||
switch (error_code) {
|
||||
case ERROR_CODE_REJECT_OTHER:
|
||||
case ERROR_CODE_REJECT_BUFFER_OVERFLOW:
|
||||
case ERROR_CODE_REJECT_INCONSISTENT_PARAMETERS:
|
||||
case ERROR_CODE_REJECT_INVALID_PARAMETER_DATA_TYPE:
|
||||
case ERROR_CODE_REJECT_INVALID_TAG:
|
||||
case ERROR_CODE_REJECT_MISSING_REQUIRED_PARAMETER:
|
||||
case ERROR_CODE_REJECT_PARAMETER_OUT_OF_RANGE:
|
||||
case ERROR_CODE_REJECT_TOO_MANY_ARGUMENTS:
|
||||
case ERROR_CODE_REJECT_UNDEFINED_ENUMERATION:
|
||||
case ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE:
|
||||
case ERROR_CODE_INVALID_DATA_ENCODING:
|
||||
case ERROR_CODE_REJECT_PROPRIETARY:
|
||||
status = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Convert a reject code to BACnet Error code
|
||||
* @param reject_code - code to be converted
|
||||
@@ -100,6 +135,9 @@ BACNET_ERROR_CODE reject_convert_to_error_code(BACNET_REJECT_REASON reject_code)
|
||||
BACNET_ERROR_CODE error_code = ERROR_CODE_REJECT_OTHER;
|
||||
|
||||
switch (reject_code) {
|
||||
case REJECT_REASON_OTHER:
|
||||
error_code = ERROR_CODE_REJECT_OTHER;
|
||||
break;
|
||||
case REJECT_REASON_BUFFER_OVERFLOW:
|
||||
error_code = ERROR_CODE_REJECT_BUFFER_OVERFLOW;
|
||||
break;
|
||||
@@ -127,8 +165,8 @@ BACNET_ERROR_CODE reject_convert_to_error_code(BACNET_REJECT_REASON reject_code)
|
||||
case REJECT_REASON_UNRECOGNIZED_SERVICE:
|
||||
error_code = ERROR_CODE_REJECT_UNRECOGNIZED_SERVICE;
|
||||
break;
|
||||
case REJECT_REASON_OTHER:
|
||||
error_code = ERROR_CODE_REJECT_OTHER;
|
||||
case REJECT_REASON_INVALID_DATA_ENCODING:
|
||||
error_code = ERROR_CODE_INVALID_DATA_ENCODING;
|
||||
break;
|
||||
default:
|
||||
if ((reject_code >= REJECT_REASON_PROPRIETARY_FIRST) &&
|
||||
|
||||
+5
-2
@@ -21,8 +21,8 @@
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*********************************************************************/
|
||||
#ifndef REJECT_H
|
||||
#define REJECT_H
|
||||
#ifndef BACNET_REJECT_H
|
||||
#define BACNET_REJECT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
@@ -37,6 +37,9 @@ extern "C" {
|
||||
BACNET_REJECT_REASON reject_convert_error_code(
|
||||
BACNET_ERROR_CODE error_code);
|
||||
BACNET_STACK_EXPORT
|
||||
bool reject_valid_error_code(
|
||||
BACNET_ERROR_CODE error_code);
|
||||
BACNET_STACK_EXPORT
|
||||
BACNET_ERROR_CODE reject_convert_to_error_code(
|
||||
BACNET_REJECT_REASON reject_code);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user