diff --git a/src/bacnet/bacenum.h b/src/bacnet/bacenum.h index 09dbdb60..3c7f6b98 100644 --- a/src/bacnet/bacenum.h +++ b/src/bacnet/bacenum.h @@ -1478,7 +1478,7 @@ typedef enum BACnet_Confirmed_Service_Choice { /* Services added after 2016 */ /* confirmed-audit-notification [32] see Alarm and Event Services */ /* audit-log-query [33] see Object Access Services */ - MAX_BACNET_CONFIRMED_SERVICE = 30 + MAX_BACNET_CONFIRMED_SERVICE = 34 } BACNET_CONFIRMED_SERVICE; /* BACnetUnconfirmedServiceChoice ::= ENUMERATED */ @@ -1515,6 +1515,7 @@ typedef enum BACnet_Services_Supported { /* Alarm and Event Services */ SERVICE_SUPPORTED_ACKNOWLEDGE_ALARM = 0, SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION = 1, + SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION_MULTIPLE = 42, SERVICE_SUPPORTED_CONFIRMED_EVENT_NOTIFICATION = 2, SERVICE_SUPPORTED_GET_ALARM_SUMMARY = 3, SERVICE_SUPPORTED_GET_ENROLLMENT_SUMMARY = 4, diff --git a/src/bacnet/basic/service/h_apdu.c b/src/bacnet/basic/service/h_apdu.c index 1f3096de..867492d0 100644 --- a/src/bacnet/basic/service/h_apdu.c +++ b/src/bacnet/basic/service/h_apdu.c @@ -76,7 +76,11 @@ static BACNET_SERVICES_SUPPORTED SERVICE_SUPPORTED_AUTHENTICATE, SERVICE_SUPPORTED_REQUEST_KEY, SERVICE_SUPPORTED_READ_RANGE, SERVICE_SUPPORTED_LIFE_SAFETY_OPERATION, SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY, - SERVICE_SUPPORTED_GET_EVENT_INFORMATION + SERVICE_SUPPORTED_GET_EVENT_INFORMATION, + SERVICE_SUPPORTED_SUBSCRIBE_COV_PROPERTY_MULTIPLE, + SERVICE_SUPPORTED_CONFIRMED_COV_NOTIFICATION_MULTIPLE, + SERVICE_SUPPORTED_CONFIRMED_AUDIT_NOTIFICATION, + SERVICE_SUPPORTED_AUDIT_LOG_QUERY }; /* a simple table for crossing the services supported */ @@ -88,7 +92,13 @@ static BACNET_SERVICES_SUPPORTED SERVICE_SUPPORTED_UNCONFIRMED_PRIVATE_TRANSFER, SERVICE_SUPPORTED_UNCONFIRMED_TEXT_MESSAGE, SERVICE_SUPPORTED_TIME_SYNCHRONIZATION, SERVICE_SUPPORTED_WHO_HAS, - SERVICE_SUPPORTED_WHO_IS, SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION + SERVICE_SUPPORTED_WHO_IS, + SERVICE_SUPPORTED_UTC_TIME_SYNCHRONIZATION, + SERVICE_SUPPORTED_WRITE_GROUP, + SERVICE_SUPPORTED_UNCONFIRMED_COV_NOTIFICATION_MULTIPLE, + SERVICE_SUPPORTED_UNCONFIRMED_AUDIT_NOTIFICATION, + SERVICE_SUPPORTED_WHO_AM_I, + SERVICE_SUPPORTED_YOU_ARE }; /* Confirmed Function Handlers */ @@ -241,16 +251,24 @@ static union { confirmed_ack_function complex; } Confirmed_ACK_Function[MAX_BACNET_CONFIRMED_SERVICE]; -void apdu_set_confirmed_simple_ack_handler( - BACNET_CONFIRMED_SERVICE service_choice, - confirmed_simple_ack_function pFunction) +/** + * @brief Determine if the BACnet service is a Simple Ack Service + * @param service_choice [in] BACnet confirmed service choice + */ +bool apdu_confirmed_simple_ack_service( + BACNET_CONFIRMED_SERVICE service_choice) { + bool status = false; + switch (service_choice) { case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM: + case SERVICE_CONFIRMED_AUDIT_NOTIFICATION: case SERVICE_CONFIRMED_COV_NOTIFICATION: + case SERVICE_CONFIRMED_COV_NOTIFICATION_MULTIPLE: case SERVICE_CONFIRMED_EVENT_NOTIFICATION: case SERVICE_CONFIRMED_SUBSCRIBE_COV: case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY: + case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY_MULTIPLE: case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION: /* Object Access Services */ case SERVICE_CONFIRMED_ADD_LIST_ELEMENT: @@ -266,40 +284,29 @@ void apdu_set_confirmed_simple_ack_handler( case SERVICE_CONFIRMED_VT_CLOSE: /* Security Services */ case SERVICE_CONFIRMED_REQUEST_KEY: - Confirmed_ACK_Function[service_choice].simple = pFunction; + status = true; break; default: break; } + + return status; +} + +void apdu_set_confirmed_simple_ack_handler( + BACNET_CONFIRMED_SERVICE service_choice, + confirmed_simple_ack_function pFunction) +{ + if (apdu_confirmed_simple_ack_service(service_choice)) { + Confirmed_ACK_Function[service_choice].simple = pFunction; + } } void apdu_set_confirmed_ack_handler( BACNET_CONFIRMED_SERVICE service_choice, confirmed_ack_function pFunction) { - switch (service_choice) { - case SERVICE_CONFIRMED_GET_ALARM_SUMMARY: - case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY: - case SERVICE_CONFIRMED_GET_EVENT_INFORMATION: - /* File Access Services */ - case SERVICE_CONFIRMED_ATOMIC_READ_FILE: - case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE: - /* Object Access Services */ - case SERVICE_CONFIRMED_CREATE_OBJECT: - case SERVICE_CONFIRMED_READ_PROPERTY: - case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: - case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: - case SERVICE_CONFIRMED_READ_RANGE: - /* Remote Device Management Services */ - case SERVICE_CONFIRMED_PRIVATE_TRANSFER: - /* Virtual Terminal Services */ - case SERVICE_CONFIRMED_VT_OPEN: - case SERVICE_CONFIRMED_VT_DATA: - /* Security Services */ - case SERVICE_CONFIRMED_AUTHENTICATE: - Confirmed_ACK_Function[service_choice].complex = pFunction; - break; - default: - break; + if (!apdu_confirmed_simple_ack_service(service_choice)) { + Confirmed_ACK_Function[service_choice].complex = pFunction; } } @@ -578,109 +585,68 @@ void apdu_handler(BACNET_ADDRESS *src, } break; case PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST: - if (apdu_len >= 2) { - service_choice = apdu[1]; - service_request = &apdu[2]; - service_request_len = apdu_len - 2; - if (apdu_unconfirmed_dcc_disabled(service_choice)) { - /* When network communications are disabled, - only DeviceCommunicationControl and - ReinitializeDevice APDUs shall be processed and no - messages shall be initiated. If communications have - been initiation disabled, then WhoIs may be - processed. */ - break; - } - if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { - if (Unconfirmed_Function[service_choice]) { - Unconfirmed_Function[service_choice]( - service_request, service_request_len, src); - } + if (apdu_len < 3) { + break; + } + service_choice = apdu[1]; + service_request = &apdu[2]; + service_request_len = apdu_len - 2; + if (apdu_unconfirmed_dcc_disabled(service_choice)) { + /* When network communications are disabled, + only DeviceCommunicationControl and + ReinitializeDevice APDUs shall be processed and no + messages shall be initiated. If communications have + been initiation disabled, then WhoIs may be + processed. */ + break; + } + if (service_choice < MAX_BACNET_UNCONFIRMED_SERVICE) { + if (Unconfirmed_Function[service_choice]) { + Unconfirmed_Function[service_choice]( + service_request, service_request_len, src); } } break; case PDU_TYPE_SIMPLE_ACK: - if (apdu_len >= 3) { - invoke_id = apdu[1]; - service_choice = apdu[2]; - switch (service_choice) { - case SERVICE_CONFIRMED_ACKNOWLEDGE_ALARM: - case SERVICE_CONFIRMED_COV_NOTIFICATION: - case SERVICE_CONFIRMED_EVENT_NOTIFICATION: - case SERVICE_CONFIRMED_SUBSCRIBE_COV: - case SERVICE_CONFIRMED_SUBSCRIBE_COV_PROPERTY: - case SERVICE_CONFIRMED_LIFE_SAFETY_OPERATION: - /* Object Access Services */ - case SERVICE_CONFIRMED_ADD_LIST_ELEMENT: - case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: - case SERVICE_CONFIRMED_DELETE_OBJECT: - case SERVICE_CONFIRMED_WRITE_PROPERTY: - case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: - /* Remote Device Management Services */ - case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: - case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: - case SERVICE_CONFIRMED_TEXT_MESSAGE: - /* Virtual Terminal Services */ - case SERVICE_CONFIRMED_VT_CLOSE: - /* Security Services */ - case SERVICE_CONFIRMED_REQUEST_KEY: - if (Confirmed_ACK_Function[service_choice].simple != - NULL) { - Confirmed_ACK_Function[service_choice].simple( - src, invoke_id); - } - tsm_free_invoke_id(invoke_id); - break; - default: - break; + if (apdu_len < 3) { + break; + } + invoke_id = apdu[1]; + service_choice = apdu[2]; + if (apdu_confirmed_simple_ack_service(service_choice)) { + if (Confirmed_ACK_Function[service_choice].simple != + NULL) { + Confirmed_ACK_Function[service_choice].simple( + src, invoke_id); } + tsm_free_invoke_id(invoke_id); } break; case PDU_TYPE_COMPLEX_ACK: - if (apdu_len >= 3) { - service_ack_data.segmented_message = - (apdu[0] & BIT(3)) ? true : false; - service_ack_data.more_follows = - (apdu[0] & BIT(2)) ? true : false; - invoke_id = service_ack_data.invoke_id = apdu[1]; - len = 2; - if (service_ack_data.segmented_message) { - service_ack_data.sequence_number = apdu[len++]; - service_ack_data.proposed_window_number = apdu[len++]; - } - service_choice = apdu[len++]; - service_request = &apdu[len]; - service_request_len = apdu_len - (uint16_t)len; - switch (service_choice) { - case SERVICE_CONFIRMED_GET_ALARM_SUMMARY: - case SERVICE_CONFIRMED_GET_ENROLLMENT_SUMMARY: - case SERVICE_CONFIRMED_GET_EVENT_INFORMATION: - /* File Access Services */ - case SERVICE_CONFIRMED_ATOMIC_READ_FILE: - case SERVICE_CONFIRMED_ATOMIC_WRITE_FILE: - /* Object Access Services */ - case SERVICE_CONFIRMED_CREATE_OBJECT: - case SERVICE_CONFIRMED_READ_PROPERTY: - case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: - case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: - case SERVICE_CONFIRMED_READ_RANGE: - case SERVICE_CONFIRMED_PRIVATE_TRANSFER: - /* Virtual Terminal Services */ - case SERVICE_CONFIRMED_VT_OPEN: - case SERVICE_CONFIRMED_VT_DATA: - /* Security Services */ - case SERVICE_CONFIRMED_AUTHENTICATE: - if (Confirmed_ACK_Function[service_choice] - .complex != NULL) { - Confirmed_ACK_Function[service_choice].complex( - service_request, service_request_len, src, - &service_ack_data); - } - tsm_free_invoke_id(invoke_id); - break; - default: - break; + if (apdu_len < 3) { + break; + } + service_ack_data.segmented_message = + (apdu[0] & BIT(3)) ? true : false; + service_ack_data.more_follows = + (apdu[0] & BIT(2)) ? true : false; + invoke_id = service_ack_data.invoke_id = apdu[1]; + len = 2; + if (service_ack_data.segmented_message) { + service_ack_data.sequence_number = apdu[len++]; + service_ack_data.proposed_window_number = apdu[len++]; + } + service_choice = apdu[len++]; + service_request = &apdu[len]; + service_request_len = apdu_len - (uint16_t)len; + if (!apdu_confirmed_simple_ack_service(service_choice)) { + if (Confirmed_ACK_Function[service_choice] + .complex != NULL) { + Confirmed_ACK_Function[service_choice].complex( + service_request, service_request_len, src, + &service_ack_data); } + tsm_free_invoke_id(invoke_id); } break; case PDU_TYPE_SEGMENT_ACK: @@ -688,49 +654,52 @@ void apdu_handler(BACNET_ADDRESS *src, we could check src to see if that matched the tsm */ tsm_free_invoke_id(invoke_id); break; - case PDU_TYPE_ERROR: - if (apdu_len >= 3) { - invoke_id = apdu[1]; - service_choice = apdu[2]; - if (apdu_complex_error(service_choice)) { - if (Error_Function[service_choice].complex) { - Error_Function[service_choice].complex(src, - invoke_id, service_choice, &apdu[3], - apdu_len - 3); - } - } else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { - len = bacerror_decode_error_class_and_code( - &apdu[3], apdu_len - 3, &error_class, &error_code); - if ((len != 0) && - (Error_Function[service_choice].error)) { - Error_Function[service_choice].error(src, invoke_id, - (BACNET_ERROR_CLASS)error_class, - (BACNET_ERROR_CODE)error_code); - } - } - tsm_free_invoke_id(invoke_id); + case PDU_TYPE_ERROR: + if (apdu_len < 3) { + break; } + invoke_id = apdu[1]; + service_choice = apdu[2]; + if (apdu_complex_error(service_choice)) { + if (Error_Function[service_choice].complex) { + Error_Function[service_choice].complex(src, + invoke_id, service_choice, &apdu[3], + apdu_len - 3); + } + } else if (service_choice < MAX_BACNET_CONFIRMED_SERVICE) { + len = bacerror_decode_error_class_and_code( + &apdu[3], apdu_len - 3, &error_class, &error_code); + if ((len != 0) && + (Error_Function[service_choice].error)) { + Error_Function[service_choice].error(src, invoke_id, + (BACNET_ERROR_CLASS)error_class, + (BACNET_ERROR_CODE)error_code); + } + } + tsm_free_invoke_id(invoke_id); break; case PDU_TYPE_REJECT: - if (apdu_len >= 3) { - invoke_id = apdu[1]; - reason = apdu[2]; - if (Reject_Function) { - Reject_Function(src, invoke_id, reason); - } - tsm_free_invoke_id(invoke_id); + if (apdu_len < 3) { + break; } + invoke_id = apdu[1]; + reason = apdu[2]; + if (Reject_Function) { + Reject_Function(src, invoke_id, reason); + } + tsm_free_invoke_id(invoke_id); break; case PDU_TYPE_ABORT: - if (apdu_len >= 3) { - server = apdu[0] & 0x01; - invoke_id = apdu[1]; - reason = apdu[2]; - if (Abort_Function) { - Abort_Function(src, invoke_id, reason, server); - } - tsm_free_invoke_id(invoke_id); + if (apdu_len < 3) { + break; } + server = apdu[0] & 0x01; + invoke_id = apdu[1]; + reason = apdu[2]; + if (Abort_Function) { + Abort_Function(src, invoke_id, reason, server); + } + tsm_free_invoke_id(invoke_id); break; default: break; diff --git a/src/bacnet/basic/service/h_apdu.h b/src/bacnet/basic/service/h_apdu.h index 728c8dcd..141f76cc 100644 --- a/src/bacnet/basic/service/h_apdu.h +++ b/src/bacnet/basic/service/h_apdu.h @@ -125,6 +125,10 @@ extern "C" { BACNET_CONFIRMED_SERVICE service_choice, confirmed_ack_function pFunction); + BACNET_STACK_EXPORT + bool apdu_confirmed_simple_ack_service( + BACNET_CONFIRMED_SERVICE service_choice); + BACNET_STACK_EXPORT void apdu_set_confirmed_simple_ack_handler( BACNET_CONFIRMED_SERVICE service_choice,