Implement parsing for proprietary events (complex-event-type) (#420)
* Implement parsing for proprietary events (complex-event-type) * Fix unit test linking for event.c --------- Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
+191
-5
@@ -40,6 +40,182 @@
|
|||||||
|
|
||||||
/** @file event.c Encode/Decode Event Notifications */
|
/** @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
|
||||||
|
* @param apdu - apdu buffer
|
||||||
|
* @param data - the event data struct to store the results in
|
||||||
|
* @return new value of "len", or negative on error
|
||||||
|
*/
|
||||||
|
static int parse_complex_event_type_values(int len, unsigned apdu_len, uint8_t *apdu, 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;
|
||||||
|
|
||||||
|
#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1)
|
||||||
|
/* we want to extract the values */
|
||||||
|
BACNET_PROPERTY_VALUE *values = data->notificationParams.complexEventType.values;
|
||||||
|
bacapp_property_value_list_init(values, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS);
|
||||||
|
|
||||||
|
BACNET_PROPERTY_VALUE *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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// we just want to discard the complex values
|
||||||
|
BACNET_PROPERTY_VALUE dummyValue;
|
||||||
|
bacapp_property_value_list_init(&dummyValue, 1);
|
||||||
|
BACNET_PROPERTY_VALUE *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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
/* end of list? */
|
||||||
|
if (decode_is_closing_tag_number(&apdu[len], 6)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
int uevent_notify_encode_apdu(
|
int uevent_notify_encode_apdu(
|
||||||
uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data)
|
uint8_t *apdu, BACNET_EVENT_NOTIFICATION_DATA *data)
|
||||||
{
|
{
|
||||||
@@ -513,6 +689,7 @@ int event_notify_decode_service_request(
|
|||||||
uint32_t enum_value = 0;
|
uint32_t enum_value = 0;
|
||||||
uint32_t len_value = 0;
|
uint32_t len_value = 0;
|
||||||
uint8_t tag_number = 0;
|
uint8_t tag_number = 0;
|
||||||
|
bool is_complex_event_type = false;
|
||||||
|
|
||||||
if (apdu_len && data) {
|
if (apdu_len && data) {
|
||||||
/* tag 0 - processIdentifier */
|
/* tag 0 - processIdentifier */
|
||||||
@@ -660,10 +837,6 @@ int event_notify_decode_service_request(
|
|||||||
if (decode_is_opening_tag_number(
|
if (decode_is_opening_tag_number(
|
||||||
&apdu[len], (uint8_t)data->eventType)) {
|
&apdu[len], (uint8_t)data->eventType)) {
|
||||||
len++;
|
len++;
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data->eventType) {
|
switch (data->eventType) {
|
||||||
case EVENT_CHANGE_OF_BITSTRING:
|
case EVENT_CHANGE_OF_BITSTRING:
|
||||||
if (-1 ==
|
if (-1 ==
|
||||||
@@ -1111,8 +1284,21 @@ int event_notify_decode_service_request(
|
|||||||
default:
|
default:
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
} else if (decode_is_opening_tag_number(&apdu[len], 6)) {
|
||||||
|
/* complex-event-type [6] SEQUENCE OF BACnetPropertyValue */
|
||||||
|
len++;
|
||||||
|
is_complex_event_type = true;
|
||||||
|
|
||||||
|
len = parse_complex_event_type_values(len, apdu_len, apdu, data);
|
||||||
|
if (len < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (decode_is_closing_tag_number(
|
if (decode_is_closing_tag_number(
|
||||||
&apdu[len], (uint8_t)data->eventType)) {
|
&apdu[len], is_complex_event_type ? 6 : (uint8_t)data->eventType)) {
|
||||||
len++;
|
len++;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -48,6 +48,20 @@ typedef enum {
|
|||||||
** Based on UnconfirmedEventNotification-Request
|
** Based on UnconfirmedEventNotification-Request
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// TODO make these configurable from env?
|
||||||
|
|
||||||
|
/** Enable decoding of complex-event-type property-values. If set to 0, the values are decoded and discarded. */
|
||||||
|
#ifndef BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS
|
||||||
|
#define BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Max complex-event-type property-values to decode. Events with more values fail to decode. */
|
||||||
|
#ifndef BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS
|
||||||
|
#define BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
typedef struct BACnet_Event_Notification_Data {
|
typedef struct BACnet_Event_Notification_Data {
|
||||||
uint32_t processIdentifier;
|
uint32_t processIdentifier;
|
||||||
BACNET_OBJECT_ID initiatingObjectIdentifier;
|
BACNET_OBJECT_ID initiatingObjectIdentifier;
|
||||||
@@ -168,6 +182,14 @@ typedef struct BACnet_Event_Notification_Data {
|
|||||||
/* OPTIONAL - Set authenticationFactor.format_type to
|
/* OPTIONAL - Set authenticationFactor.format_type to
|
||||||
AUTHENTICATION_FACTOR_MAX if not being used */
|
AUTHENTICATION_FACTOR_MAX if not being used */
|
||||||
} accessEvent;
|
} accessEvent;
|
||||||
|
#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1)
|
||||||
|
/*
|
||||||
|
* complex-event-type - a sequence of values, used for proprietary event types
|
||||||
|
*/
|
||||||
|
struct {
|
||||||
|
BACNET_PROPERTY_VALUE values[BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS];
|
||||||
|
} complexEventType;
|
||||||
|
#endif
|
||||||
} notificationParams;
|
} notificationParams;
|
||||||
} BACNET_EVENT_NOTIFICATION_DATA;
|
} BACNET_EVENT_NOTIFICATION_DATA;
|
||||||
|
|
||||||
|
|||||||
@@ -35,16 +35,28 @@ add_executable(${PROJECT_NAME}
|
|||||||
${SRC_DIR}/bacnet/event.c
|
${SRC_DIR}/bacnet/event.c
|
||||||
# Support files and stubs (pathname alphabetical)
|
# Support files and stubs (pathname alphabetical)
|
||||||
${SRC_DIR}/bacnet/authentication_factor.c
|
${SRC_DIR}/bacnet/authentication_factor.c
|
||||||
|
${SRC_DIR}/bacnet/bacaddr.c
|
||||||
|
${SRC_DIR}/bacnet/bacapp.c
|
||||||
${SRC_DIR}/bacnet/bacdcode.c
|
${SRC_DIR}/bacnet/bacdcode.c
|
||||||
|
${SRC_DIR}/bacnet/bacdest.c
|
||||||
|
${SRC_DIR}/bacnet/bacdevobjpropref.c
|
||||||
${SRC_DIR}/bacnet/bacint.c
|
${SRC_DIR}/bacnet/bacint.c
|
||||||
${SRC_DIR}/bacnet/bacreal.c
|
${SRC_DIR}/bacnet/bacreal.c
|
||||||
${SRC_DIR}/bacnet/bacstr.c
|
${SRC_DIR}/bacnet/bacstr.c
|
||||||
${SRC_DIR}/bacnet/bacpropstates.c
|
${SRC_DIR}/bacnet/bacpropstates.c
|
||||||
${SRC_DIR}/bacnet/bacdevobjpropref.c
|
|
||||||
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
${SRC_DIR}/bacnet/basic/sys/bigend.c
|
||||||
${SRC_DIR}/bacnet/datetime.c
|
${SRC_DIR}/bacnet/datetime.c
|
||||||
${SRC_DIR}/bacnet/basic/sys/days.c
|
${SRC_DIR}/bacnet/basic/sys/days.c
|
||||||
${SRC_DIR}/bacnet/timestamp.c
|
${SRC_DIR}/bacnet/timestamp.c
|
||||||
|
# Dependencies of bacapp.c
|
||||||
|
${SRC_DIR}/bacnet/bactext.c
|
||||||
|
${SRC_DIR}/bacnet/indtext.c
|
||||||
|
${SRC_DIR}/bacnet/weeklyschedule.c
|
||||||
|
${SRC_DIR}/bacnet/bactimevalue.c
|
||||||
|
${SRC_DIR}/bacnet/dailyschedule.c
|
||||||
|
${SRC_DIR}/bacnet/lighting.c
|
||||||
|
${SRC_DIR}/bacnet/timestamp.c
|
||||||
|
${SRC_DIR}/bacnet/hostnport.c
|
||||||
# Test and test library files
|
# Test and test library files
|
||||||
./src/main.c
|
./src/main.c
|
||||||
${ZTST_DIR}/ztest_mock.c
|
${ZTST_DIR}/ztest_mock.c
|
||||||
|
|||||||
@@ -30,16 +30,28 @@ if(BOARD STREQUAL unit_testing)
|
|||||||
|
|
||||||
get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH)
|
get_filename_component(BACNET_SRC ${BACNET_SRC_PATH} PATH)
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
|
${BACNET_SRC}/bacaddr.c
|
||||||
|
${BACNET_SRC}/bacapp.c
|
||||||
|
${BACNET_SRC}/bacstr.c
|
||||||
${BACNET_SRC}/bacdcode.c
|
${BACNET_SRC}/bacdcode.c
|
||||||
|
${BACNET_SRC}/bacdest.c
|
||||||
${BACNET_SRC}/bacstr.c
|
${BACNET_SRC}/bacstr.c
|
||||||
${BACNET_SRC}/bacint.c
|
${BACNET_SRC}/bacint.c
|
||||||
${BACNET_SRC}/bacreal.c
|
${BACNET_SRC}/bacreal.c
|
||||||
${BACNET_SRC}/datetime.c
|
${BACNET_SRC}/datetime.c
|
||||||
${BACNET_SRC}/timestamp.c
|
|
||||||
${BACNET_SRC}/basic/sys/days.c
|
${BACNET_SRC}/basic/sys/days.c
|
||||||
${BACNET_SRC}/bacdevobjpropref.c
|
${BACNET_SRC}/bacdevobjpropref.c
|
||||||
${BACNET_SRC}/authentication_factor.c
|
${BACNET_SRC}/authentication_factor.c
|
||||||
${BACNET_SRC}/bacpropstates.c
|
${BACNET_SRC}/bacpropstates.c
|
||||||
|
# Dependencies of bacapp.c
|
||||||
|
${BACNET_SRC}/bactext.c
|
||||||
|
${BACNET_SRC}/indtext.c
|
||||||
|
${BACNET_SRC}/weeklyschedule.c
|
||||||
|
${BACNET_SRC}/bactimevalue.c
|
||||||
|
${BACNET_SRC}/dailyschedule.c
|
||||||
|
${BACNET_SRC}/lighting.c
|
||||||
|
${BACNET_SRC}/timestamp.c
|
||||||
|
${BACNET_SRC}/hostnport.c
|
||||||
)
|
)
|
||||||
|
|
||||||
set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf")
|
set(CONF_FILE "${CONF_FILE};prj.unit_testing.conf")
|
||||||
|
|||||||
Reference in New Issue
Block a user