Added AddListElement and RemoveListElement services (#418)

* Added AddListElement and RemoveListElement services

Added list-element codec and unit tests.
Added BACnetDestination codec and unit tests.
Added RecipientList handling to NotificationClass object.
Added AddListElement and RemoveListElement client example app.

* Fix defects found by scan-build and CPPCHECK

* Update ports errors found during CI builds

* Update zephy os test build missing files in CMakeLists

---------

Co-authored-by: Ondřej Hruška <ondra@ondrovo.com>
Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-04-16 14:12:11 -05:00
committed by GitHub
parent e517df0d47
commit ced9dff0f2
116 changed files with 4615 additions and 563 deletions
@@ -116,14 +116,16 @@ static object_functions_t Object_Table[] = {
Device_Read_Property_Local, NULL /* Write_Property */,
Device_Property_Lists, NULL /* ReadRangeInfo */, NULL /* Iterator */,
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */ },
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#if (BACNET_PROTOCOL_REVISION >= 17)
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
Network_Port_Object_Name, Network_Port_Read_Property,
Network_Port_Write_Property, Network_Port_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#endif
{ MAX_BACNET_OBJECT_TYPE, NULL /* Init */, NULL /* Count */,
NULL /* Index_To_Instance */, NULL /* Valid_Instance */,
@@ -131,7 +133,8 @@ static object_functions_t Object_Table[] = {
NULL /* Write_Property */, NULL /* Property_Lists */,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */ }
NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }
};
/** Glue function to let the Device object, when called by a handler,
+129 -32
View File
@@ -97,14 +97,16 @@ static object_functions_t My_Object_Table[] = {
Device_Read_Property_Local, Device_Write_Property_Local,
Device_Property_Lists, DeviceGetRRInfo, NULL /* Iterator */,
NULL /* Value_Lists */, NULL /* COV */, NULL /* COV Clear */,
NULL /* Intrinsic Reporting */ },
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */ },
#if (BACNET_PROTOCOL_REVISION >= 17)
{ OBJECT_NETWORK_PORT, Network_Port_Init, Network_Port_Count,
Network_Port_Index_To_Instance, Network_Port_Valid_Instance,
Network_Port_Object_Name, Network_Port_Read_Property,
Network_Port_Write_Property, Network_Port_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#endif
{ OBJECT_ANALOG_INPUT, Analog_Input_Init, Analog_Input_Count,
Analog_Input_Index_To_Instance, Analog_Input_Valid_Instance,
@@ -112,39 +114,45 @@ static object_functions_t My_Object_Table[] = {
Analog_Input_Write_Property, Analog_Input_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Analog_Input_Encode_Value_List, Analog_Input_Change_Of_Value,
Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting },
Analog_Input_Change_Of_Value_Clear, Analog_Input_Intrinsic_Reporting,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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,
Analog_Value_Write_Property, Analog_Value_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Analog_Value_Encode_Value_List, Analog_Value_Change_Of_Value,
Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting },
Analog_Value_Change_Of_Value_Clear, Analog_Value_Intrinsic_Reporting,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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,
Binary_Input_Write_Property, Binary_Input_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Binary_Input_Encode_Value_List, Binary_Input_Change_Of_Value,
Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */ },
Binary_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ OBJECT_CHARACTERSTRING_VALUE, CharacterString_Value_Init,
CharacterString_Value_Count, CharacterString_Value_Index_To_Instance,
CharacterString_Value_Valid_Instance, CharacterString_Value_Object_Name,
@@ -154,18 +162,21 @@ static object_functions_t My_Object_Table[] = {
NULL /* Iterator */, CharacterString_Value_Encode_Value_List,
CharacterString_Value_Change_Of_Value,
CharacterString_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */ },
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#if defined(INTRINSIC_REPORTING)
{ OBJECT_NOTIFICATION_CLASS, Notification_Class_Init,
Notification_Class_Count, Notification_Class_Index_To_Instance,
@@ -173,86 +184,99 @@ static object_functions_t My_Object_Table[] = {
Notification_Class_Read_Property, Notification_Class_Write_Property,
Notification_Class_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
Notification_Class_Add_List_Element,
Notification_Class_Remove_List_Element },
#endif
{ OBJECT_LIFE_SAFETY_POINT, Life_Safety_Point_Init, Life_Safety_Point_Count,
Life_Safety_Point_Index_To_Instance, Life_Safety_Point_Valid_Instance,
Life_Safety_Point_Object_Name, Life_Safety_Point_Read_Property,
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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ OBJECT_MULTI_STATE_OUTPUT, Multistate_Output_Init,
Multistate_Output_Count, Multistate_Output_Index_To_Instance,
Multistate_Output_Valid_Instance, Multistate_Output_Object_Name,
Multistate_Output_Read_Property, Multistate_Output_Write_Property,
Multistate_Output_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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,
Multistate_Value_Write_Property, Multistate_Value_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */,
Multistate_Value_Encode_Value_List, Multistate_Value_Change_Of_Value,
Multistate_Value_Change_Of_Value_Clear,
NULL /* Intrinsic Reporting */ },
Multistate_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#if (BACNET_PROTOCOL_REVISION >= 14)
{ OBJECT_LIGHTING_OUTPUT, Lighting_Output_Init, Lighting_Output_Count,
Lighting_Output_Index_To_Instance, Lighting_Output_Valid_Instance,
Lighting_Output_Object_Name, Lighting_Output_Read_Property,
Lighting_Output_Write_Property, Lighting_Output_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#endif
#if (BACNET_PROTOCOL_REVISION >= 24)
{ OBJECT_COLOR, Color_Init, Color_Count, Color_Index_To_Instance,
Color_Valid_Instance, Color_Object_Name, Color_Read_Property,
Color_Write_Property, Color_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#endif
#if defined(BACFILE)
{ OBJECT_FILE, bacfile_init, bacfile_count, bacfile_index_to_instance,
bacfile_valid_instance, bacfile_object_name, bacfile_read_property,
bacfile_write_property, BACfile_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
#endif
{ OBJECT_OCTETSTRING_VALUE, OctetString_Value_Init, OctetString_Value_Count,
OctetString_Value_Index_To_Instance, OctetString_Value_Valid_Instance,
OctetString_Value_Object_Name, OctetString_Value_Read_Property,
OctetString_Value_Write_Property, OctetString_Value_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ OBJECT_POSITIVE_INTEGER_VALUE, PositiveInteger_Value_Init,
PositiveInteger_Value_Count, PositiveInteger_Value_Index_To_Instance,
PositiveInteger_Value_Valid_Instance, PositiveInteger_Value_Object_Name,
@@ -260,26 +284,29 @@ static object_functions_t My_Object_Table[] = {
PositiveInteger_Value_Write_Property,
PositiveInteger_Value_Property_Lists, NULL /* ReadRangeInfo */,
NULL /* Iterator */, NULL /* Value_Lists */, NULL /* COV */,
NULL /* COV Clear */, NULL /* Intrinsic Reporting */ },
NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ },
{ 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 /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */ }
};
/** Glue function to let the Device object, when called by a handler,
@@ -1686,7 +1713,7 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
*/
bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
{
bool status = false; /* Ever the pessamist! */
bool status = false; /* Ever the pessimist! */
struct object_functions *pObject = NULL;
/* initialize the default return values */
@@ -1722,6 +1749,76 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
return (status);
}
/**
* @brief AddListElement from an object list property
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
*/
int Device_Add_List_Element(
BACNET_LIST_ELEMENT_DATA * list_element)
{
int status = BACNET_STATUS_ERROR;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(list_element->object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(list_element->object_instance)) {
if (pObject->Object_Add_List_Element) {
status = pObject->Object_Add_List_Element(list_element);
} else {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/**
* @brief RemoveListElement from an object list property
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
*/
int Device_Remove_List_Element(
BACNET_LIST_ELEMENT_DATA * list_element)
{
int status = BACNET_STATUS_ERROR;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(list_element->object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(list_element->object_instance)) {
if (pObject->Object_Remove_List_Element) {
status = pObject->Object_Remove_List_Element(list_element);
} else {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return status;
}
/** Looks up the requested Object, and fills the Property Value list.
* If the Object or Property can't be found, returns false.
* @ingroup ObjHelpers
@@ -1734,7 +1831,7 @@ bool Device_Encode_Value_List(BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_VALUE *value_list)
{
bool status = false; /* Ever the pessamist! */
bool status = false; /* Ever the pessimist! */
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(object_type);
+11
View File
@@ -34,6 +34,7 @@
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/bacdef.h"
#include "bacnet/bacenum.h"
#include "bacnet/list_element.h"
#include "bacnet/wp.h"
#include "bacnet/rd.h"
#include "bacnet/rp.h"
@@ -165,6 +166,8 @@ typedef struct object_functions {
object_cov_function Object_COV;
object_cov_clear_function Object_COV_Clear;
object_intrinsic_reporting_function Object_Intrinsic_Reporting;
list_element_function Object_Add_List_Element;
list_element_function Object_Remove_List_Element;
} object_functions_t;
/* String Lengths - excluding any nul terminator */
@@ -431,6 +434,14 @@ extern "C" {
bool Device_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_STACK_EXPORT
int Device_Add_List_Element(
BACNET_LIST_ELEMENT_DATA *list_element);
BACNET_STACK_EXPORT
int Device_Remove_List_Element(
BACNET_LIST_ELEMENT_DATA *list_element);
BACNET_STACK_EXPORT
bool DeviceGetRRInfo(
BACNET_READ_RANGE_DATA * pRequest, /* Info on the request */
+402 -407
View File
@@ -38,8 +38,10 @@
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/bacdest.h"
#include "bacnet/basic/services.h"
#include "bacnet/config.h"
#include "bacnet/datetime.h"
#include "bacnet/basic/object/device.h"
#include "bacnet/event.h"
#include "bacnet/basic/services.h"
@@ -57,6 +59,8 @@
#if defined(INTRINSIC_REPORTING)
static NOTIFICATION_CLASS_INFO NC_Info[MAX_NOTIFICATION_CLASSES];
/* buffer for sending event messages */
static uint8_t Event_Buffer[MAX_APDU];
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Notification_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
@@ -89,34 +93,16 @@ void Notification_Class_Init(void)
memset(&NC_Info[NotifyIdx], 0x00, sizeof(NOTIFICATION_CLASS_INFO));
/* set the basic parameters */
NC_Info[NotifyIdx].Ack_Required = 0;
NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] =
255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] =
255; /* The lowest priority for Normal message. */
NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] =
255; /* PRINTF lowest priority for Normal message. */
/* configure for every day, all day long */
for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) {
NC_Info[NotifyIdx].Recipient_List->ValidDays |= (1 << i);
/* The lowest priority for Normal message = 255 */
NC_Info[NotifyIdx].Priority[TRANSITION_TO_OFFNORMAL] = 255;
NC_Info[NotifyIdx].Priority[TRANSITION_TO_FAULT] = 255;
NC_Info[NotifyIdx].Priority[TRANSITION_TO_NORMAL] = 255;
/* note: default uses wildcard device destination */
for (i = 0; i < NC_MAX_RECIPIENTS; i++) {
BACNET_DESTINATION *destination;
destination = &NC_Info[NotifyIdx].Recipient_List[i];
bacnet_destination_default_init(destination);
}
NC_Info[NotifyIdx].Recipient_List->FromTime.hour = 0;
NC_Info[NotifyIdx].Recipient_List->FromTime.min = 0;
NC_Info[NotifyIdx].Recipient_List->FromTime.sec = 0;
NC_Info[NotifyIdx].Recipient_List->FromTime.hundredths = 0;
NC_Info[NotifyIdx].Recipient_List->ToTime.hour = 23;
NC_Info[NotifyIdx].Recipient_List->ToTime.min = 59;
NC_Info[NotifyIdx].Recipient_List->ToTime.sec = 59;
NC_Info[NotifyIdx].Recipient_List->ToTime.hundredths = 0;
NC_Info[NotifyIdx].Recipient_List->Transitions =
TRANSITION_TO_OFFNORMAL_MASKED | TRANSITION_TO_FAULT_MASKED |
TRANSITION_TO_NORMAL_MASKED;
NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false;
NC_Info[NotifyIdx].Recipient_List->ConfirmedNotify = false;
NC_Info[NotifyIdx].Recipient_List->Recipient.RecipientType =
RECIPIENT_TYPE_DEVICE;
/* initialize to *wildcard* device instance - invalid! */
NC_Info[NotifyIdx].Recipient_List->Recipient._.DeviceIdentifier =
BACNET_MAX_INSTANCE;
}
return;
@@ -184,12 +170,12 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_CHARACTER_STRING char_string;
BACNET_OCTET_STRING octet_string;
BACNET_BIT_STRING bit_string;
uint8_t *apdu = NULL;
uint8_t u8Val;
int idx;
int apdu_len = 0; /* return value */
uint16_t apdu_max = 0;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
@@ -197,6 +183,7 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
}
apdu = rpdata->application_data;
apdu_max = rpdata->application_data_len;
CurrentNotify =
&NC_Info[Notification_Class_Instance_To_Index(rpdata->object_instance)];
@@ -262,103 +249,32 @@ int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
break;
case PROP_RECIPIENT_LIST:
/* encode all entry of Recipient_List */
/* get the size of all entry of Recipient_List */
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_DESTINATION *RecipientEntry;
int i = 0;
/* get pointer of current element for Recipient_List - easier
* for use */
RecipientEntry = &CurrentNotify->Recipient_List[idx];
if (RecipientEntry->Recipient.RecipientType !=
RECIPIENT_TYPE_NOTINITIALIZED) {
/* Valid Days - BACnetDaysOfWeek - [bitstring] monday-sunday
*/
u8Val = 0x01;
bitstring_init(&bit_string);
for (i = 0; i < MAX_BACNET_DAYS_OF_WEEK; i++) {
if (RecipientEntry->ValidDays & u8Val)
bitstring_set_bit(&bit_string, i, true);
else
bitstring_set_bit(&bit_string, i, false);
u8Val <<= 1; /* next day */
}
apdu_len += encode_application_bitstring(
&apdu[apdu_len], &bit_string);
/* From Time */
apdu_len += encode_application_time(
&apdu[apdu_len], &RecipientEntry->FromTime);
/* To Time */
apdu_len += encode_application_time(
&apdu[apdu_len], &RecipientEntry->ToTime);
/*
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
} */
/* CHOICE - device [0] BACnetObjectIdentifier */
if (RecipientEntry->Recipient.RecipientType ==
RECIPIENT_TYPE_DEVICE) {
apdu_len += encode_context_object_id(&apdu[apdu_len], 0,
OBJECT_DEVICE,
RecipientEntry->Recipient._.DeviceIdentifier);
}
/* CHOICE - address [1] BACnetAddress */
else if (RecipientEntry->Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
/* opening tag 1 */
apdu_len += encode_opening_tag(&apdu[apdu_len], 1);
/* network-number Unsigned16, */
apdu_len += encode_application_unsigned(&apdu[apdu_len],
RecipientEntry->Recipient._.Address.net);
/* mac-address OCTET STRING */
if (RecipientEntry->Recipient._.Address.net) {
octetstring_init(&octet_string,
RecipientEntry->Recipient._.Address.adr,
RecipientEntry->Recipient._.Address.len);
} else {
octetstring_init(&octet_string,
RecipientEntry->Recipient._.Address.mac,
RecipientEntry->Recipient._.Address.mac_len);
}
apdu_len += encode_application_octet_string(
&apdu[apdu_len], &octet_string);
/* closing tag 1 */
apdu_len += encode_closing_tag(&apdu[apdu_len], 1);
} else {
;
} /* shouldn't happen */
/* Process Identifier - Unsigned32 */
apdu_len += encode_application_unsigned(
&apdu[apdu_len], RecipientEntry->ProcessIdentifier);
/* Issue Confirmed Notifications - boolean */
apdu_len += encode_application_boolean(
&apdu[apdu_len], RecipientEntry->ConfirmedNotify);
/* Transitions - BACnet Event Transition Bits [bitstring] */
u8Val = RecipientEntry->Transitions;
bitstring_init(&bit_string);
bitstring_set_bit(&bit_string, TRANSITION_TO_OFFNORMAL,
(u8Val & TRANSITION_TO_OFFNORMAL_MASKED) ? true
: false);
bitstring_set_bit(&bit_string, TRANSITION_TO_FAULT,
(u8Val & TRANSITION_TO_FAULT_MASKED) ? true : false);
bitstring_set_bit(&bit_string, TRANSITION_TO_NORMAL,
(u8Val & TRANSITION_TO_NORMAL_MASKED) ? true : false);
apdu_len += encode_application_bitstring(
&apdu[apdu_len], &bit_string);
BACNET_DESTINATION *Destination;
BACNET_RECIPIENT *Recipient;
Destination = &CurrentNotify->Recipient_List[idx];
Recipient = &Destination->Recipient;
if (!bacnet_recipient_device_wildcard(Recipient)) {
apdu_len += bacnet_destination_encode(NULL, Destination);
}
}
if (apdu_len > apdu_max) {
/* Abort response */
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
break;
}
/* size fits, therefore, encode all entry of Recipient_List */
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_DESTINATION *Destination;
BACNET_RECIPIENT *Recipient;
Destination = &CurrentNotify->Recipient_List[idx];
Recipient = &Destination->Recipient;
if (!bacnet_recipient_device_wildcard(Recipient)) {
apdu_len +=
bacnet_destination_encode(&apdu[apdu_len], Destination);
}
}
break;
@@ -481,232 +397,26 @@ bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
break;
case PROP_RECIPIENT_LIST:
memset(&TmpNotify, 0x00, sizeof(NOTIFICATION_CLASS_INFO));
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_DESTINATION *destination;
destination = &TmpNotify.Recipient_List[idx];
bacnet_destination_default_init(destination);
}
idx = 0;
iOffset = 0;
/* decode all packed */
while (iOffset < wp_data->application_data_len) {
/* Decode Valid Days */
len = bacapp_decode_application_data(
BACNET_DESTINATION *destination;
destination = &TmpNotify.Recipient_List[idx];
len = bacnet_destination_decode(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->application_data_len, destination);
if (len == BACNET_STATUS_REJECT) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
if (value.type.Bit_String.bits_used == MAX_BACNET_DAYS_OF_WEEK)
/* store value */
TmpNotify.Recipient_List[idx].ValidDays =
value.type.Bit_String.value[0];
else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_OTHER;
return false;
}
iOffset += len;
/* Decode From Time */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].FromTime = value.type.Time;
iOffset += len;
/* Decode To Time */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) || (value.tag != BACNET_APPLICATION_TAG_TIME)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ToTime = value.type.Time;
iOffset += len;
/* context tag [0] - Device */
if (decode_is_context_tag(
&wp_data->application_data[iOffset], 0)) {
TmpNotify.Recipient_List[idx].Recipient.RecipientType =
RECIPIENT_TYPE_DEVICE;
/* Decode Network Number */
len = bacapp_decode_context_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value,
PROP_RECIPIENT_LIST);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_OBJECT_ID)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].Recipient._.DeviceIdentifier =
value.type.Object_Id.instance;
iOffset += len;
}
/* opening tag [1] - Recipient */
else if (decode_is_opening_tag_number(
&wp_data->application_data[iOffset], 1)) {
iOffset++;
TmpNotify.Recipient_List[idx].Recipient.RecipientType =
RECIPIENT_TYPE_ADDRESS;
/* Decode Network Number */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].Recipient._.Address.net =
value.type.Unsigned_Int;
iOffset += len;
/* Decode Address */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_OCTET_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
if (TmpNotify.Recipient_List[idx].Recipient._.Address.net ==
0) {
memcpy(TmpNotify.Recipient_List[idx]
.Recipient._.Address.mac,
value.type.Octet_String.value,
value.type.Octet_String.length);
TmpNotify.Recipient_List[idx]
.Recipient._.Address.mac_len =
value.type.Octet_String.length;
} else {
memcpy(TmpNotify.Recipient_List[idx]
.Recipient._.Address.adr,
value.type.Octet_String.value,
value.type.Octet_String.length);
TmpNotify.Recipient_List[idx].Recipient._.Address.len =
value.type.Octet_String.length;
}
iOffset += len;
/* closing tag [1] - Recipient */
if (decode_is_closing_tag_number(
&wp_data->application_data[iOffset], 1))
iOffset++;
else {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
} else {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* Process Identifier */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_UNSIGNED_INT)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ProcessIdentifier =
value.type.Unsigned_Int;
iOffset += len;
/* Issue Confirmed Notifications */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BOOLEAN)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
/* store value */
TmpNotify.Recipient_List[idx].ConfirmedNotify =
value.type.Boolean;
iOffset += len;
/* Transitions */
len = bacapp_decode_application_data(
&wp_data->application_data[iOffset],
wp_data->application_data_len, &value);
if ((len == 0) ||
(value.tag != BACNET_APPLICATION_TAG_BIT_STRING)) {
/* Bad decode, wrong tag or following required parameter
* missing */
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_INVALID_DATA_TYPE;
return false;
}
if (value.type.Bit_String.bits_used ==
MAX_BACNET_EVENT_TRANSITION)
/* store value */
TmpNotify.Recipient_List[idx].Transitions =
value.type.Bit_String.value[0];
else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_OTHER;
return false;
}
iOffset += len;
/* Increasing element of list */
if (++idx >= NC_MAX_RECIPIENTS) {
wp_data->error_class = ERROR_CLASS_RESOURCES;
@@ -714,33 +424,24 @@ bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
return false;
}
}
/* Decoded all recipient list */
/* copy elements from temporary object */
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++) {
BACNET_ADDRESS src = { 0 };
unsigned max_apdu = 0;
int32_t DeviceID;
uint32_t device_id;
BACNET_DESTINATION *destination;
BACNET_RECIPIENT *recipient;
CurrentNotify->Recipient_List[idx] =
TmpNotify.Recipient_List[idx];
if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
/* copy Device_ID */
DeviceID = CurrentNotify->Recipient_List[idx]
.Recipient._.DeviceIdentifier;
address_bind_request(DeviceID, &max_apdu, &src);
} else if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
/* copy Address */
/* src =
* CurrentNotify->Recipient_List[idx].Recipient._.Address;
*/
/* address_bind_request(BACNET_MAX_INSTANCE, &max_apdu,
* &src); */
destination = &CurrentNotify->Recipient_List[idx];
bacnet_destination_copy(
destination, &TmpNotify.Recipient_List[idx]);
recipient = &destination->Recipient;
if (bacnet_recipient_device_valid(recipient)) {
device_id = recipient->type.device.instance;
address_bind_request(device_id, &max_apdu, &src);
} else if (recipient->tag == BACNET_RECIPIENT_TAG_ADDRESS) {
/* nothing to do - we have the address */
}
}
status = true;
@@ -791,18 +492,22 @@ static bool IsRecipientActive(
case EVENT_STATE_OFFNORMAL:
case EVENT_STATE_HIGH_LIMIT:
case EVENT_STATE_LOW_LIMIT:
if (!(pBacDest->Transitions & TRANSITION_TO_OFFNORMAL_MASKED))
if (bitstring_bit(
&pBacDest->Transitions, TRANSITION_TO_OFFNORMAL)) {
return false;
}
break;
case EVENT_STATE_FAULT:
if (!(pBacDest->Transitions & TRANSITION_TO_FAULT_MASKED))
if (bitstring_bit(&pBacDest->Transitions, TRANSITION_TO_FAULT)) {
return false;
}
break;
case EVENT_STATE_NORMAL:
if (!(pBacDest->Transitions & TRANSITION_TO_NORMAL_MASKED))
if (bitstring_bit(&pBacDest->Transitions, TRANSITION_TO_NORMAL)) {
return false;
}
break;
default:
@@ -810,12 +515,12 @@ static bool IsRecipientActive(
}
/* get actual date and time */
Device_getCurrentDateTime(&DateTime);
datetime_local(&DateTime.date, &DateTime.time, NULL, NULL);
/* valid Days */
if (!((0x01 << (DateTime.date.wday - 1)) & pBacDest->ValidDays))
if (!(bitstring_bit(&pBacDest->ValidDays, (DateTime.date.wday - 1)))) {
return false;
}
/* valid FromTime */
if (datetime_compare_time(&DateTime.time, &pBacDest->FromTime) < 0)
return false;
@@ -890,10 +595,9 @@ void Notification_Class_common_reporting_function(
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (index = 0; index < NC_MAX_RECIPIENTS; index++, pBacDest++) {
/* check if recipient is defined */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_NOTINITIALIZED)
break; /* recipient doesn't defined - end of list */
if (bacnet_recipient_device_wildcard(&pBacDest->Recipient)) {
continue;
}
if (IsRecipientActive(pBacDest, event_data->toState)) {
BACNET_ADDRESS dest;
uint32_t device_id;
@@ -903,18 +607,17 @@ void Notification_Class_common_reporting_function(
event_data->processIdentifier = pBacDest->ProcessIdentifier;
/* send notification */
if (pBacDest->Recipient.RecipientType == RECIPIENT_TYPE_DEVICE) {
if (pBacDest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) {
/* send notification to the specified device */
device_id = pBacDest->Recipient._.DeviceIdentifier;
device_id = pBacDest->Recipient.type.device.instance;
PRINTF("Notification Class[%u]: send notification to %u\n",
event_data->notificationClass, (unsigned)device_id);
if (pBacDest->ConfirmedNotify == true)
Send_CEvent_Notify(device_id, event_data);
else if (address_get_by_device(device_id, &max_apdu, &dest))
Send_UEvent_Notify(
Handler_Transmit_Buffer, event_data, &dest);
} else if (pBacDest->Recipient.RecipientType ==
RECIPIENT_TYPE_ADDRESS) {
Send_UEvent_Notify(Event_Buffer, event_data, &dest);
} else if (pBacDest->Recipient.tag ==
BACNET_RECIPIENT_TAG_ADDRESS) {
PRINTF("Notification Class[%u]: send notification to ADDR\n",
event_data->notificationClass);
/* send notification to the address indicated */
@@ -922,9 +625,8 @@ void Notification_Class_common_reporting_function(
if (address_get_device_id(&dest, &device_id))
Send_CEvent_Notify(device_id, event_data);
} else {
dest = pBacDest->Recipient._.Address;
Send_UEvent_Notify(
Handler_Transmit_Buffer, event_data, &dest);
dest = pBacDest->Recipient.type.address;
Send_UEvent_Notify(Event_Buffer, event_data, &dest);
}
}
}
@@ -935,39 +637,332 @@ void Notification_Class_common_reporting_function(
/* It should be called periodically (example once per minute). */
void Notification_Class_find_recipient(void)
{
NOTIFICATION_CLASS_INFO *CurrentNotify;
BACNET_DESTINATION *pBacDest;
NOTIFICATION_CLASS_INFO *notification;
BACNET_DESTINATION *destination;
BACNET_RECIPIENT *recipient;
BACNET_ADDRESS src = { 0 };
unsigned max_apdu = 0;
uint32_t notify_index;
uint32_t DeviceID;
uint8_t idx;
uint32_t device_id;
unsigned i, j;
for (notify_index = 0; notify_index < MAX_NOTIFICATION_CLASSES;
notify_index++) {
/* pointer to current notification */
CurrentNotify =
&NC_Info[Notification_Class_Instance_To_Index(notify_index)];
/* pointer to first recipient */
pBacDest = &CurrentNotify->Recipient_List[0];
for (idx = 0; idx < NC_MAX_RECIPIENTS; idx++, pBacDest++) {
if (CurrentNotify->Recipient_List[idx].Recipient.RecipientType ==
RECIPIENT_TYPE_DEVICE) {
/* Device ID */
DeviceID = CurrentNotify->Recipient_List[idx]
.Recipient._.DeviceIdentifier;
/* Send who_ is request only when address of device is unknown.
*/
if (DeviceID < BACNET_MAX_INSTANCE) {
/* note: BACNET_MAX_INSTANCE = wildcard, not valid */
if (!address_bind_request(DeviceID, &max_apdu, &src)) {
Send_WhoIs(DeviceID, DeviceID);
}
for (i = 0; i < MAX_NOTIFICATION_CLASSES; i++) {
notification = &NC_Info[i];
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
destination = &notification->Recipient_List[j];
recipient = &destination->Recipient;
if (bacnet_recipient_device_valid(recipient)) {
device_id = recipient->type.device.instance;
if (!address_bind_request(device_id, &max_apdu, &src)) {
/* Send who_ is request only when
address of device is unknown. */
Send_WhoIs(device_id, device_id);
}
} else if (CurrentNotify->Recipient_List[idx]
.Recipient.RecipientType == RECIPIENT_TYPE_ADDRESS) {
}
}
}
}
/**
* @brief AddListElement from an object list property
* @ingroup ObjHelpers
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return #BACNET_STATUS_OK or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
* @note 15.1.2 Service Procedure
* After verifying the validity of the request, the responding BACnet-user
* shall attempt to modify the object identified in the
* 'Object Identifier' parameter. If the identified object exists
* and has the property specified in the 'Property Identifier' parameter,
* and, if present, has the array element specified in the
* 'Property Array Index' parameter, an attempt shall be made to add all of
* the elements specified in the 'List of Elements' parameter to the
* specified property or array element of the property. If this
* attempt is successful, a 'Result(+)' primitive shall be issued.
*
* When comparing elements in the 'List of Elements' parameter with
* elements in the specified property or array element of the
* property, the complete element shall be compared unless the
* property description specifies otherwise. If one or more of the
* elements is already present in the BACnetLIST, it shall be updated
* with the provided element, that is, the existing element is
* over-written with the provided element. Optionally, if the provided
* element is exactly the same as the existing element in every
* way, it can be ignored, that is, not added to the BACnetLIST.
* Ignoring an element that already exists shall not cause the
* service to fail.
*
* If the specified object does not exist, the specified property
* does not exist, the specified array element does not exist, or the
* specified property or array element is not a BACnetLIST, then
* the service shall fail and a 'Result(-)' response primitive shall
* be issued. If one or more elements cannot be added to, or updated
* in, the BACnetLIST, a 'Result(-)' response primitive shall be
* issued and no elements shall be added to, or updated in, the BACnetLIST.
*
* The effect of this service shall be to add to, or update in,
* the BACnetLIST all of the specified elements, or to neither add nor
* update any elements at all.
*/
int Notification_Class_Add_List_Element(
BACNET_LIST_ELEMENT_DATA * list_element)
{
NOTIFICATION_CLASS_INFO *notification = NULL;
BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 };
uint8_t *application_data = NULL;
int application_data_len = 0, len = 0;
uint32_t notify_index = 0;
unsigned index = 0;
unsigned element_count = 0, new_element_count = 0;
unsigned added_element_count = 0, same_element_count = 0;
bool same_element = false;
unsigned i = 0, j = 0;
if (!list_element) {
return BACNET_STATUS_ABORT;
}
if (list_element->object_property != PROP_RECIPIENT_LIST) {
list_element->error_class = ERROR_CLASS_SERVICES;
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
return BACNET_STATUS_ERROR;
}
if (list_element->array_index != BACNET_ARRAY_ALL) {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return BACNET_STATUS_ERROR;
}
notify_index =
Notification_Class_Instance_To_Index(list_element->object_instance);
if (notify_index < MAX_NOTIFICATION_CLASSES) {
notification = &NC_Info[notify_index];
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return BACNET_STATUS_ERROR;
}
/* get the current size of all entry of Recipient_List */
for (index = 0; index < NC_MAX_RECIPIENTS; index++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &notification->Recipient_List[index];
r1 = &d1->Recipient;
if (!bacnet_recipient_device_wildcard(r1)) {
element_count++;
}
}
/* decode the elements */
new_element_count = 0;
application_data = list_element->application_data;
application_data_len = list_element->application_data_len;
while (application_data_len > 0) {
len = bacnet_destination_decode(application_data,
application_data_len, &recipient_list[index]);
if (len > 0) {
new_element_count++;
application_data_len -= len;
} else {
list_element->first_failed_element_number = new_element_count;
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING;
return BACNET_STATUS_ERROR;
}
}
if (new_element_count == 0) {
return BACNET_STATUS_OK;
}
/* determine the same and added recipient counts */
same_element_count = 0;
added_element_count = 0;
for (i = 0; i < new_element_count; i++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &recipient_list[i];
r1 = &d1->Recipient;
same_element = false;
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
BACNET_DESTINATION *d2;
BACNET_RECIPIENT *r2;
d2 = &notification->Recipient_List[j];
r2 = &d2->Recipient;
if (bacnet_recipient_same(r1, r2)) {
same_element = true;
break;
}
}
if (same_element) {
same_element_count++;
} else {
added_element_count++;
if ((added_element_count+element_count) > NC_MAX_RECIPIENTS) {
list_element->first_failed_element_number = 1 + i;
list_element->error_class = ERROR_CLASS_RESOURCES;
list_element->error_code =
ERROR_CODE_NO_SPACE_TO_ADD_LIST_ELEMENT;
return BACNET_STATUS_ERROR;
}
}
}
/* update existing and add new */
for (i = 0; i < new_element_count; i++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &recipient_list[i];
r1 = &d1->Recipient;
same_element = false;
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
BACNET_DESTINATION *d2;
BACNET_RECIPIENT *r2;
d2 = &notification->Recipient_List[j];
r2 = &d2->Recipient;
if (bacnet_recipient_same(r1, r2)) {
/* update existing element */
same_element = true;
bacnet_destination_copy(d2, d1);
}
}
if (!same_element) {
/* add new element to next free slot */
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
BACNET_DESTINATION *d2;
BACNET_RECIPIENT *r2;
d2 = &notification->Recipient_List[j];
r2 = &d2->Recipient;
if (!bacnet_recipient_device_wildcard(r2)) {
bacnet_destination_copy(d2, d1);
break;
}
}
}
}
return BACNET_STATUS_OK;
}
/**
* @brief RemoveListElement from an object list property
* @ingroup ObjHelpers
* @param list_element [in] Pointer to the BACnet_List_Element_Data structure,
* which is packed with the information from the request.
* @return The length of the apdu encoded or #BACNET_STATUS_ERROR or
* #BACNET_STATUS_ABORT or #BACNET_STATUS_REJECT.
* @note After verifying the validity of the request, the responding
* BACnet-user shall attempt to modify the object identified in the
* 'Object Identifier' parameter. If the identified object exists and it
* has the property specified in the 'Property Identifier' parameter,
* and if present has the array element specified in the 'Property Array Index'
* parameter, an attempt shall be made to remove the
* elements in the 'List of Elements' from the property or array element
* of the property of the object.
*
* When comparing elements of the service with entries in the affected
* BACnetLIST, the complete element shall be compared
* unless the property description specifies otherwise. If one or more
* of the elements does not exist or cannot be removed because
* of insufficient authority, none of the elements shall be removed and
* a 'Result(-)' response primitive shall be issued.
*/
int Notification_Class_Remove_List_Element(
BACNET_LIST_ELEMENT_DATA * list_element)
{
NOTIFICATION_CLASS_INFO *notification = NULL;
uint32_t notify_index = 0;
unsigned index = 0;
BACNET_DESTINATION recipient_list[NC_MAX_RECIPIENTS] = { 0 };
uint8_t *application_data = NULL;
int application_data_len = 0, len = 0;
unsigned element_count = 0;
unsigned remove_element_count = 0;
bool same_element = false;
unsigned i = 0, j = 0;
if (!list_element) {
return BACNET_STATUS_ABORT;
}
if (list_element->object_property != PROP_RECIPIENT_LIST) {
list_element->error_class = ERROR_CLASS_SERVICES;
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_A_LIST;
return BACNET_STATUS_ERROR;
}
if (list_element->array_index != BACNET_ARRAY_ALL) {
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
return BACNET_STATUS_ERROR;
}
notify_index =
Notification_Class_Instance_To_Index(list_element->object_instance);
if (notify_index < MAX_NOTIFICATION_CLASSES) {
notification = &NC_Info[notify_index];
} else {
list_element->error_class = ERROR_CLASS_OBJECT;
list_element->error_code = ERROR_CODE_UNKNOWN_OBJECT;
return BACNET_STATUS_ERROR;
}
/* get the current size of all entry of Recipient_List */
for (index = 0; index < NC_MAX_RECIPIENTS; index++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &notification->Recipient_List[index];
r1 = &d1->Recipient;
if (!bacnet_recipient_device_wildcard(r1)) {
element_count++;
}
}
/* decode the elements */
application_data = list_element->application_data;
application_data_len = list_element->application_data_len;
while (application_data_len > 0) {
len = bacnet_destination_decode(application_data,
application_data_len, &recipient_list[index]);
if (len > 0) {
remove_element_count++;
application_data_len -= len;
} else {
list_element->first_failed_element_number = remove_element_count;
list_element->error_class = ERROR_CLASS_PROPERTY;
list_element->error_code = ERROR_CODE_INVALID_DATA_ENCODING;
return BACNET_STATUS_ERROR;
}
}
/* determine if one or more of the elements does not exist */
for (i = 0; i < remove_element_count; i++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &recipient_list[i];
r1 = &d1->Recipient;
same_element = false;
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
BACNET_DESTINATION *d2;
BACNET_RECIPIENT *r2;
d2 = &notification->Recipient_List[j];
r2 = &d2->Recipient;
if (bacnet_recipient_same(r1, r2)) {
same_element = true;
}
}
if (!same_element) {
list_element->first_failed_element_number = 1 + i;
list_element->error_class = ERROR_CLASS_SERVICES;
list_element->error_code = ERROR_CODE_LIST_ELEMENT_NOT_FOUND;
return BACNET_STATUS_ERROR;
}
}
/* remove any matching elements */
for (i = 0; i < remove_element_count; i++) {
BACNET_DESTINATION *d1;
BACNET_RECIPIENT *r1;
d1 = &recipient_list[i];
r1 = &d1->Recipient;
for (j = 0; j < NC_MAX_RECIPIENTS; j++) {
BACNET_DESTINATION *d2;
BACNET_RECIPIENT *r2;
d2 = &notification->Recipient_List[j];
r2 = &d2->Recipient;
if (bacnet_recipient_same(r1, r2)) {
bacnet_destination_default_init(d2);
}
}
}
return BACNET_STATUS_OK;
}
#endif /* defined(INTRINSIC_REPORTING) */
+75 -115
View File
@@ -1,152 +1,112 @@
/**************************************************************************
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
*
* Copyright (C) 2011 Krzysztof Malorny <malornykrzysztof@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
#ifndef NC_H
#define NC_H
#include "bacnet/bacnet_stack_exports.h"
#include "bacnet/bacdest.h"
#include "bacnet/event.h"
#include "bacnet/list_element.h"
#include "bacnet/rp.h"
#include "bacnet/wp.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define NC_RESCAN_RECIPIENTS_SECS 60
#define NC_RESCAN_RECIPIENTS_SECS 60
/* max "length" of recipient_list */
#define NC_MAX_RECIPIENTS 10
/* Recipient types */
typedef enum {
RECIPIENT_TYPE_NOTINITIALIZED = 0,
RECIPIENT_TYPE_DEVICE = 1,
RECIPIENT_TYPE_ADDRESS = 2
} NC_RECIPIENT_TYPE;
#if defined(INTRINSIC_REPORTING)
/* BACnetRecipient structure */
/*
BACnetRecipient ::= CHOICE {
device [0] BACnetObjectIdentifier,
address [1] BACnetAddress
}
*/
typedef struct BACnet_Recipient {
uint8_t RecipientType; /* Type of Recipient */
union {
uint32_t DeviceIdentifier;
BACNET_ADDRESS Address;
} _;
} BACNET_RECIPIENT;
/* BACnetDestination structure */
typedef struct BACnet_Destination {
uint8_t ValidDays;
BACNET_TIME FromTime;
BACNET_TIME ToTime;
BACNET_RECIPIENT Recipient;
uint32_t ProcessIdentifier;
uint8_t Transitions;
bool ConfirmedNotify;
} BACNET_DESTINATION;
/* Structure containing configuration for a Notification Class */
typedef struct Notification_Class_info {
uint8_t Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */
uint8_t Ack_Required; /* BACnetEventTransitionBits */
BACNET_DESTINATION Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */
} NOTIFICATION_CLASS_INFO;
typedef struct Notification_Class_info {
uint8_t
Priority[MAX_BACNET_EVENT_TRANSITION]; /* BACnetARRAY[3] of Unsigned */
uint8_t Ack_Required; /* BACnetEventTransitionBits */
BACNET_DESTINATION
Recipient_List[NC_MAX_RECIPIENTS]; /* List of BACnetDestination */
} NOTIFICATION_CLASS_INFO;
/* Indicates whether the transaction has been confirmed */
typedef struct Acked_info {
bool bIsAcked; /* true when transitions is acked */
BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */
} ACKED_INFO;
typedef struct Acked_info {
bool bIsAcked; /* true when transitions is acked */
BACNET_DATE_TIME Time_Stamp; /* time stamp of when a alarm was generated */
} ACKED_INFO;
/* Information needed to send AckNotification */
typedef struct Ack_Notification {
bool bSendAckNotify; /* true if need to send AckNotification */
uint8_t EventState;
} ACK_NOTIFICATION;
typedef struct Ack_Notification {
bool bSendAckNotify; /* true if need to send AckNotification */
uint8_t EventState;
} ACK_NOTIFICATION;
BACNET_STACK_EXPORT
void Notification_Class_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
void Notification_Class_Init(void);
BACNET_STACK_EXPORT
void Notification_Class_Property_Lists(
const int **pRequired,
const int **pOptional,
const int **pProprietary);
bool Notification_Class_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Notification_Class_Count(void);
BACNET_STACK_EXPORT
uint32_t Notification_Class_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Notification_Class_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Notification_Class_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
void Notification_Class_Init(
void);
BACNET_STACK_EXPORT
int Notification_Class_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
bool Notification_Class_Valid_Instance(
uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Notification_Class_Count(
void);
BACNET_STACK_EXPORT
uint32_t Notification_Class_Index_To_Instance(
unsigned index);
BACNET_STACK_EXPORT
unsigned Notification_Class_Instance_To_Index(
uint32_t object_instance);
BACNET_STACK_EXPORT
bool Notification_Class_Object_Name(
uint32_t object_instance,
BACNET_CHARACTER_STRING * object_name);
BACNET_STACK_EXPORT
bool Notification_Class_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data);
BACNET_STACK_EXPORT
int Notification_Class_Read_Property(
BACNET_READ_PROPERTY_DATA * rpdata);
BACNET_STACK_EXPORT
int Notification_Class_Add_List_Element(BACNET_LIST_ELEMENT_DATA *list_element);
BACNET_STACK_EXPORT
bool Notification_Class_Write_Property(
BACNET_WRITE_PROPERTY_DATA * wp_data);
BACNET_STACK_EXPORT
int Notification_Class_Remove_List_Element(
BACNET_LIST_ELEMENT_DATA *list_element);
BACNET_STACK_EXPORT
void Notification_Class_Get_Priorities(
uint32_t Object_Instance,
uint32_t * pPriorityArray);
BACNET_STACK_EXPORT
void Notification_Class_Get_Priorities(
uint32_t Object_Instance, uint32_t *pPriorityArray);
BACNET_STACK_EXPORT
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA * event_data);
BACNET_STACK_EXPORT
void Notification_Class_common_reporting_function(
BACNET_EVENT_NOTIFICATION_DATA *event_data);
BACNET_STACK_EXPORT
void Notification_Class_find_recipient(
void);
BACNET_STACK_EXPORT
void Notification_Class_find_recipient(void);
#endif /* defined(INTRINSIC_REPORTING) */
#ifdef __cplusplus
}
#endif /* __cplusplus */
+211
View File
@@ -0,0 +1,211 @@
/**
* @file
* @brief AddListElement and RemoveListElement service application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* 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/list_element.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 AddListElement 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_Add_List_Element() succeeds
* - an Error if Device_Add_List_Element() 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_add_list_element(uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
BACNET_LIST_ELEMENT_DATA list_element = { 0 };
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
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("AddListElement: 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("AddListElement: Segmented message. Sending Abort!\n");
status = false;
}
if (status) {
/* decode the service request only */
len = list_element_decode_service_request(
service_request, service_len, &list_element);
if (len > 0) {
debug_perror("AddListElement: type=%lu instance=%lu property=%lu "
"index=%ld\n",
(unsigned long)list_element.object_type,
(unsigned long)list_element.object_instance,
(unsigned long)list_element.object_property,
(long)list_element.array_index);
} else {
debug_perror("AddListElement: 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("AddListElement: Bad Encoding. Sending Abort!\n");
status = false;
}
if (status) {
if (Device_Add_List_Element(&list_element)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_ADD_LIST_ELEMENT);
debug_perror("AddListElement: Sending Simple Ack!\n");
} else {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id, SERVICE_CONFIRMED_ADD_LIST_ELEMENT,
list_element.error_class, list_element.error_code);
debug_perror("AddListElement: Sending Error!\n");
}
}
}
/* 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(
"AddListElement: Failed to send PDU (%s)!\n", strerror(errno));
}
return;
}
/**
* @brief Handler for a RemoveListElement 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_Remove_List_Element() succeeds
* - an Error if Device_Remove_List_Element() 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_remove_list_element(uint8_t *service_request,
uint16_t service_len,
BACNET_ADDRESS *src,
BACNET_CONFIRMED_SERVICE_DATA *service_data)
{
BACNET_LIST_ELEMENT_DATA list_element = { 0 };
BACNET_NPDU_DATA npdu_data;
BACNET_ADDRESS my_address;
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("RemoveListElement: 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("RemoveListElement: Segmented message. Sending Abort!\n");
status = false;
}
if (status) {
/* decode the service request only */
len = list_element_decode_service_request(
service_request, service_len, &list_element);
if (len > 0) {
debug_perror("RemoveListElement: type=%lu instance=%lu "
"property=%lu index=%ld\n",
(unsigned long)list_element.object_type,
(unsigned long)list_element.object_instance,
(unsigned long)list_element.object_property,
(long)list_element.array_index);
} else {
debug_perror("RemoveListElement: 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("RemoveListElement: Bad Encoding. Sending Abort!\n");
status = false;
}
if (status) {
if (Device_Remove_List_Element(&list_element)) {
len = encode_simple_ack(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT);
debug_perror("RemoveListElement: Sending Simple Ack!\n");
} else {
len = bacerror_encode_apdu(&Handler_Transmit_Buffer[pdu_len],
service_data->invoke_id,
SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT,
list_element.error_class, list_element.error_code);
debug_perror("RemoveListElement: Sending Error!\n");
}
}
}
/* 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(
"RemoveListElement: Failed to send PDU (%s)!\n", strerror(errno));
}
return;
}
+43
View File
@@ -0,0 +1,43 @@
/**
* @file
* @brief API for AddListElement and RemoveListElement application handlers
* @author Steve Karg <skarg@users.sourceforge.net>
* @date December 2022
* @section LICENSE
*
* Copyright (C) 2022 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef HANDLER_LIST_ELEMENT_H
#define HANDLER_LIST_ELEMENT_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_add_list_element(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA *service_data);
BACNET_STACK_EXPORT
void handler_remove_list_element(
uint8_t * service_request,
uint16_t service_len,
BACNET_ADDRESS * src,
BACNET_CONFIRMED_SERVICE_DATA *service_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+282
View File
@@ -0,0 +1,282 @@
/**
* @file
* @brief AddListElement and RemoveListElement service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
* @section LICENSE
*
* Copyright (C) 2023 Steve Karg <skarg@users.sourceforge.net>
*
* 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/list_element.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 ListElement service message
* @param service [in] AddListElement or RemoveListElement
* @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.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_List_Element_Request_Data(
BACNET_CONFIRMED_SERVICE service,
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index)
{
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_LIST_ELEMENT_DATA data;
BACNET_NPDU_DATA npdu_data;
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.object_property = object_property;
data.array_index = array_index;
data.application_data_len = application_data_len;
data.application_data = application_data;
len = list_element_encode_service_request(
&Handler_Transmit_Buffer[pdu_len], &data);
pdu_len += len;
/* will it fit in the sender?
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) {
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 an AddListElement 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.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_Add_List_Element_Request_Data(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index)
{
return Send_List_Element_Request_Data(
SERVICE_CONFIRMED_ADD_LIST_ELEMENT,
device_id,
object_type,
object_instance,
object_property,
application_data,
application_data_len,
array_index);
}
/**
* @brief Send a RemoveListElement 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.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
* @return the invoke ID for confirmed request, or zero on failure
*/
uint8_t Send_Remove_List_Element_Request_Data(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index)
{
return Send_List_Element_Request_Data(
SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT,
device_id,
object_type,
object_instance,
object_property,
application_data,
application_data_len,
array_index);
}
/**
* @brief Sends an AddListElement 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.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
*/
uint8_t Send_Add_List_Element_Request(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE *object_value,
uint32_t array_index)
{
uint8_t application_data[MAX_APDU] = { 0 };
int apdu_len = 0, len = 0;
while (object_value) {
debug_printf("AddListElement service: "
"%s tag=%d\n",
(object_value->context_specific ? "context" : "application"),
(int)(object_value->context_specific ? object_value->context_tag
: object_value->tag));
len = bacapp_encode_data(&application_data[apdu_len], object_value);
if ((len + apdu_len) < MAX_APDU) {
apdu_len += len;
} else {
return 0;
}
object_value = object_value->next;
}
return Send_Add_List_Element_Request_Data(device_id, object_type,
object_instance, object_property, &application_data[0], apdu_len,
array_index);
}
/**
* @brief Sends an RemoveListElement 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.
* @param object_property [in] Property to be written.
* @param object_value [in] The value to be written to the property.
* @param array_index [in] Optional: if the Property is an array,
* - 0 for the array size
* - 1 to n for individual array members
* - BACNET_ARRAY_ALL (~0) for the array value to be ignored (not sent)
* @return invoke id of outgoing message, or 0 on failure.
*/
uint8_t Send_Remove_List_Element_Request(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE *object_value,
uint32_t array_index)
{
uint8_t application_data[MAX_APDU] = { 0 };
int apdu_len = 0, len = 0;
while (object_value) {
debug_perror("RemoveListElement service: "
"%s tag=%d\n",
(object_value->context_specific ? "context" : "application"),
(int)(object_value->context_specific ? object_value->context_tag
: object_value->tag));
len = bacapp_encode_data(&application_data[apdu_len], object_value);
if ((len + apdu_len) < MAX_APDU) {
apdu_len += len;
} else {
return 0;
}
object_value = object_value->next;
}
return Send_Remove_List_Element_Request_Data(device_id, object_type,
object_instance, object_property, &application_data[0], apdu_len,
array_index);
}
+75
View File
@@ -0,0 +1,75 @@
/**
* @file
* @brief AddListElement and RemoveListElement service initiation
* @author Steve Karg <skarg@users.sourceforge.net>
* @date January 2023
* @section LICENSE
*
* Copyright (C) 2023 Steve Karg <skarg@users.sourceforge.net>
*
* SPDX-License-Identifier: MIT
*/
#ifndef SEND_LIST_ELEMENT_H
#define SEND_LIST_ELEMENT_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_List_Element_Request_Data(
BACNET_CONFIRMED_SERVICE service,
uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index);
BACNET_STACK_EXPORT
uint8_t Send_Add_List_Element_Request_Data(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index);
BACNET_STACK_EXPORT
uint8_t Send_Add_List_Element_Request(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE *object_value,
uint32_t array_index);
BACNET_STACK_EXPORT
uint8_t Send_Remove_List_Element_Request_Data(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
uint8_t *application_data,
int application_data_len,
uint32_t array_index);
BACNET_STACK_EXPORT
uint8_t Send_Remove_List_Element_Request(uint32_t device_id,
BACNET_OBJECT_TYPE object_type,
uint32_t object_instance,
BACNET_PROPERTY_ID object_property,
BACNET_APPLICATION_DATA_VALUE *object_value,
uint32_t array_index);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+2
View File
@@ -48,6 +48,7 @@
#include "bacnet/basic/service/h_getevent_a.h"
#include "bacnet/basic/service/h_iam.h"
#include "bacnet/basic/service/h_ihave.h"
#include "bacnet/basic/service/h_list_element.h"
#include "bacnet/basic/service/h_lso.h"
#include "bacnet/basic/service/h_noserv.h"
#include "bacnet/basic/service/h_rd.h"
@@ -79,6 +80,7 @@
#include "bacnet/basic/service/s_getevent.h"
#include "bacnet/basic/service/s_iam.h"
#include "bacnet/basic/service/s_ihave.h"
#include "bacnet/basic/service/s_list_element.h"
#include "bacnet/basic/service/s_lso.h"
#include "bacnet/basic/service/s_rd.h"
#include "bacnet/basic/service/s_readrange.h"