Feature/add basic structured view object (#641)

* Added basic structured view object and unit test. Added example structured view into server example.

* Removed creation of objects from basic device object into the server example
This commit is contained in:
Steve Karg
2024-05-14 14:51:45 -05:00
committed by GitHub
parent c21c99fef3
commit affb358139
19 changed files with 1337 additions and 43 deletions
+2
View File
@@ -259,6 +259,8 @@ add_library(${PROJECT_NAME}
src/bacnet/basic/object/piv.h
src/bacnet/basic/object/schedule.c
src/bacnet/basic/object/schedule.h
src/bacnet/basic/object/structured_view.c
src/bacnet/basic/object/structured_view.h
src/bacnet/basic/object/time_value.c
src/bacnet/basic/object/time_value.h
src/bacnet/basic/object/trendlog.c
+1
View File
@@ -40,6 +40,7 @@ BACNET_OBJECT_SRC := \
$(BACNET_OBJECT_DIR)/time_value.c \
$(BACNET_OBJECT_DIR)/trendlog.c \
$(BACNET_OBJECT_DIR)/schedule.c \
$(BACNET_OBJECT_DIR)/structured_view.c \
$(BACNET_OBJECT_DIR)/access_credential.c \
$(BACNET_OBJECT_DIR)/access_door.c \
$(BACNET_OBJECT_DIR)/access_point.c \
+1
View File
@@ -34,6 +34,7 @@ SRC = main.c \
$(BACNET_OBJECT_DIR)/time_value.c \
$(BACNET_OBJECT_DIR)/trendlog.c \
$(BACNET_OBJECT_DIR)/schedule.c \
$(BACNET_OBJECT_DIR)/structured_view.c \
$(BACNET_OBJECT_DIR)/access_credential.c \
$(BACNET_OBJECT_DIR)/access_door.c \
$(BACNET_OBJECT_DIR)/access_point.c \
+95 -3
View File
@@ -31,12 +31,13 @@
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/apdu.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bactext.h"
#include "bacnet/dcc.h"
#include "bacnet/getevent.h"
#include "bacnet/iam.h"
#include "bacnet/npdu.h"
#include "bacnet/getevent.h"
#include "bacnet/version.h"
/* some demo stuff needed */
#include "bacnet/basic/binding/address.h"
@@ -58,6 +59,7 @@
#endif
#include "bacnet/basic/object/lc.h"
#include "bacnet/basic/object/trendlog.h"
#include "bacnet/basic/object/structured_view.h"
#if defined(INTRINSIC_REPORTING)
#include "bacnet/basic/object/nc.h"
#endif /* defined(INTRINSIC_REPORTING) */
@@ -92,12 +94,103 @@ static struct mstimer BACnet_Object_Timer;
/** Buffer used for receiving */
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
/* configure an example structured view object subordinate list */
#if (BACNET_PROTOCOL_REVISION >= 4)
#define LIGHTING_OBJECT_WATTS OBJECT_ACCUMULATOR
#else
#define LIGHTING_OBJECT_WATTS OBJECT_ANALOG_INPUT
#endif
#if (BACNET_PROTOCOL_REVISION >= 6)
#define LIGHTING_OBJECT_ADR OBJECT_LOAD_CONTROL
#else
#define LIGHTING_OBJECT_ADR OBJECT_MULTISTATE_OUTPUT
#endif
#if (BACNET_PROTOCOL_REVISION >= 6)
#define LIGHTING_OBJECT_ADR OBJECT_LOAD_CONTROL
#else
#define LIGHTING_OBJECT_ADR OBJECT_MULTISTATE_OUTPUT
#endif
#if (BACNET_PROTOCOL_REVISION >= 14)
#define LIGHTING_OBJECT_SCENE OBJECT_CHANNEL
#define LIGHTING_OBJECT_LIGHT OBJECT_LIGHTING_OUTPUT
#else
#define LIGHTING_OBJECT_SCENE OBJECT_ANALOG_VALUE
#define LIGHTING_OBJECT_LIGHT OBJECT_ANALOG_OUTPUT
#endif
#if (BACNET_PROTOCOL_REVISION >= 16)
#define LIGHTING_OBJECT_RELAY OBJECT_BINARY_LIGHTING_OUTPUT
#else
#define LIGHTING_OBJECT_RELAY OBJECT_BINARY_OUTPUT
#endif
static BACNET_SUBORDINATE_DATA Lighting_Subordinate[] =
{
{0, LIGHTING_OBJECT_WATTS, 1, "watt-hours", 0, 0, NULL},
{0, LIGHTING_OBJECT_ADR, 1, "demand-response", 0, 0, NULL},
{0, LIGHTING_OBJECT_SCENE, 1, "scene", 0, 0, NULL},
{0, LIGHTING_OBJECT_LIGHT, 1, "light", 0, 0, NULL},
{0, LIGHTING_OBJECT_RELAY, 1, "relay", 0, 0, NULL},
#if (BACNET_PROTOCOL_REVISION >= 24)
{0, OBJECT_COLOR, 1, "color", 0, 0, NULL},
{0, OBJECT_COLOR_TEMPERATURE, 1, "color-temperature", 0, 0, NULL},
#endif
};
/**
* @brief Update the strcutured view static data with device ID and linked lists
* @param device_id Device Instance to assign to every subordinate
*/
static void Structured_View_Update(void)
{
uint32_t device_id, instance;
BACNET_DEVICE_OBJECT_REFERENCE represents = { 0 };
size_t i;
device_id = Device_Object_Instance_Number();
for (i = 0; i < ARRAY_SIZE(Lighting_Subordinate); i++) {
/* link the lists */
if (i < (ARRAY_SIZE(Lighting_Subordinate)-1)) {
Lighting_Subordinate[i].next = &Lighting_Subordinate[i+1];
}
/* update the device instance to internal */
Lighting_Subordinate[i].Device_Instance = device_id;
/* update the common node data */
Lighting_Subordinate[i].Node_Type = BACNET_NODE_ROOM;
Lighting_Subordinate[i].Relationship = BACNET_RELATIONSHIP_CONTAINS;
}
instance = Structured_View_Index_To_Instance(0);
Structured_View_Subordinate_List_Set(instance, Lighting_Subordinate);
/* In some cases, the Structure View object will abstractly represent
this entity by itself, and this property will either be absent,
unconfigured, or point to itself. */
represents.deviceIdentifier.type = OBJECT_NONE;
represents.deviceIdentifier.instance = BACNET_MAX_INSTANCE;
represents.objectIdentifier.type = OBJECT_DEVICE;
represents.objectIdentifier.instance = Device_Object_Instance_Number();
Structured_View_Represents_Set(instance, &represents);
Structured_View_Node_Type_Set(instance, BACNET_NODE_ROOM);
}
/** Initialize the handlers we will utilize.
* @see Device_Init, apdu_set_unconfirmed_handler, apdu_set_confirmed_handler
*/
static void Init_Service_Handlers(void)
{
BACNET_CREATE_OBJECT_DATA object_data = { 0 };
unsigned int i = 0;
Device_Init(NULL);
/* create some dynamically created objects as examples */
object_data.object_instance = BACNET_MAX_INSTANCE;
for (i = 0; i <= BACNET_OBJECT_TYPE_LAST; i++) {
object_data.object_type = i;
if (Device_Create_Object(&object_data)) {
printf("Created object %s-%u\n", bactext_object_type_name(i),
(unsigned)object_data.object_instance);
}
}
/* update structured view with this device instance */
Structured_View_Update();
/* we need to handle who-is to support dynamic device binding */
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is);
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has);
@@ -296,7 +389,6 @@ int main(int argc, char *argv[])
if (Device_Object_Name(Device_Object_Instance_Number(), &DeviceName)) {
printf("BACnet Device Name: %s\n", DeviceName.value);
}
dlenv_init();
atexit(datalink_cleanup);
/* broadcast an I-Am on startup */
@@ -198,6 +198,7 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\osv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\piv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\structured_view.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\time_value.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c" />
</ItemGroup>
@@ -84,6 +84,9 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\structured_view.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\osv.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -76,6 +76,7 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\osv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\piv.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\structured_view.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\service\h_alarm_ack.c" />
<ClCompile Include="..\..\..\..\src\bacnet\basic\service\h_apdu.c" />
@@ -384,6 +384,9 @@
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\schedule.c">
<Filter>Source Files\src\bacnet\basic\object</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\structured_view.c">
<Filter>Source Files\src\bacnet\basic\object</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\bacnet\basic\object\trendlog.c">
<Filter>Source Files\src\bacnet\basic\object</Filter>
</ClCompile>
+61 -12
View File
@@ -2048,9 +2048,58 @@ typedef enum BACnetNodeType {
BACNET_NODE_COLLECTION = 8,
BACNET_NODE_PROPERTY = 9,
BACNET_NODE_FUNCTIONAL = 10,
BACNET_NODE_OTHER = 11
BACNET_NODE_OTHER = 11,
BACNET_NODE_SUBSYSTEM = 12,
BACNET_NODE_BUILDING = 13,
BACNET_NODE_FLOOR = 14,
BACNET_NODE_SECTION = 15,
BACNET_NODE_MODULE = 16,
BACNET_NODE_TREE = 17,
BACNET_NODE_MEMBER = 18,
BACNET_NODE_PROTOCOL = 19,
BACNET_NODE_ROOM = 20,
BACNET_NODE_ZONE = 21
} BACNET_NODE_TYPE;
typedef enum BACnetRelationship {
BACNET_RELATIONSHIP_UNKNOWN = 0,
BACNET_RELATIONSHIP_DEFAULT = 1,
BACNET_RELATIONSHIP_CONTAINS = 2,
BACNET_RELATIONSHIP_CONTAINED_BY = 3,
BACNET_RELATIONSHIP_USES = 4,
BACNET_RELATIONSHIP_USED_BY = 5,
BACNET_RELATIONSHIP_COMMANDS = 6,
BACNET_RELATIONSHIP_COMMANDED_BY = 7,
BACNET_RELATIONSHIP_ADJUSTS = 8,
BACNET_RELATIONSHIP_ADJUSTED_BY = 9,
BACNET_RELATIONSHIP_INGRESS = 10,
BACNET_RELATIONSHIP_EGRESS = 11,
BACNET_RELATIONSHIP_SUPPLIES_AIR = 12,
BACNET_RELATIONSHIP_RECEIVES_AIR = 13,
BACNET_RELATIONSHIP_SUPPLIES_HOT_AIR = 14,
BACNET_RELATIONSHIP_RECEIVES_HOT_AIR = 15,
BACNET_RELATIONSHIP_SUPPLIES_COOL_AIR = 16,
BACNET_RELATIONSHIP_RECEIVES_COOL_AIR = 17,
BACNET_RELATIONSHIP_SUPPLIES_POWER = 18,
BACNET_RELATIONSHIP_RECEIVES_POWER = 19,
BACNET_RELATIONSHIP_SUPPLIES_GAS = 20,
BACNET_RELATIONSHIP_RECEIVES_GAS = 21,
BACNET_RELATIONSHIP_SUPPLIES_WATER = 22,
BACNET_RELATIONSHIP_RECEIVES_WATER = 23,
BACNET_RELATIONSHIP_SUPPLIES_HOT_WATER = 24,
BACNET_RELATIONSHIP_RECEIVES_HOT_WATER = 25,
BACNET_RELATIONSHIP_SUPPLIES_COOL_WATER = 26,
BACNET_RELATIONSHIP_RECEIVES_COOL_WATER = 27,
BACNET_RELATIONSHIP_SUPPLIES_STEAM = 28,
BACNET_RELATIONSHIP_RECEIVES_STEAM = 29,
BACNET_RELATIONSHIP_RESERVED_MAX = 1023,
BACNET_RELATIONSHIP_PROPRIETARY_MIN = 1024,
BACNET_RELATIONSHIP_PROPRIETARY_MAX = 65535
/* Enumerated values 0-1023 are reserved for definition by ASHRAE.
Enumerated values 1024-65535 may be used by others subject
to the procedures and constraints described in Clause 23. */
} BACNET_RELATIONSHIP;
typedef enum BACnetShedState {
BACNET_SHED_INACTIVE = 0,
BACNET_SHED_REQUEST_PENDING = 1,
@@ -2107,8 +2156,8 @@ typedef enum BACnetBinaryLightingPV {
BINARY_LIGHTING_PV_WARN_RELINQUISH = 4,
BINARY_LIGHTING_PV_STOP = 5,
BINARY_LIGHTING_PV_MAX = 6,
/* -- Enumerated values 0-63 are reserved for definition by ASHRAE.
-- Enumerated values 64-255 may be used by others
/* -- Enumerated values 0-63 are reserved for definition by ASHRAE.
-- Enumerated values 64-255 may be used by others
-- subject to the procedures and constraints described in Clause 23. */
BINARY_LIGHTING_PV_PROPRIETARY_MIN = 64,
BINARY_LIGHTING_PV_PROPRIETARY_MAX = 255
@@ -2315,8 +2364,8 @@ typedef enum BACnetAccessZoneOccupancyState {
ACCESS_ZONE_OCCUPANCY_STATE_ABOVE_UPPER_LIMIT = 4,
ACCESS_ZONE_OCCUPANCY_STATE_DISABLED = 5,
ACCESS_ZONE_OCCUPANCY_STATE_NOT_SUPPORTED = 6,
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
Enumerated values 64-65535 may be used by others subject
/* Enumerated values 0-63 are reserved for definition by ASHRAE.
Enumerated values 64-65535 may be used by others subject
to the procedures and constraints described in Clause 23. */
ACCESS_ZONE_OCCUPANCY_STATE_PROPRIETARY_MIN = 64,
ACCESS_ZONE_OCCUPANCY_STATE_PROPRIETARY_MAX = 65535
@@ -2624,18 +2673,18 @@ typedef enum BACnetEscalatorFault {
ESCALATOR_FAULT_PROPRIETARY_MAX = 65535
} BACNET_ESCALATOR_FAULT;
typedef enum BACnetEscalatorMode {
typedef enum BACnetEscalatorMode {
ESCALATOR_MODE_UNKNOWN = 0,
ESCALATOR_MODE_STOP = 1,
ESCALATOR_MODE_UP = 2,
ESCALATOR_MODE_STOP = 1,
ESCALATOR_MODE_UP = 2,
ESCALATOR_MODE_DOWN = 3,
ESCALATOR_MODE_INSPECTION = 4,
ESCALATOR_MODE_INSPECTION = 4,
ESCALATOR_MODE_OUT_OF_SERVICE = 5,
/* Enumerated values 0-1023 are reserved for definition by ASHRAE.
Enumerated values 1024-65535 may be used by others subject to
the procedures and constraints described in Clause 23. */
ESCALATOR_MODE_PROPRIETARY_MIN = 1024,
ESCALATOR_MODE_PROPRIETARY_MAX = 65535
ESCALATOR_MODE_PROPRIETARY_MAX = 65535
} BACNET_ESCALATOR_MODE;
typedef enum BACnetEscalatorOperationDirection {
@@ -2779,8 +2828,8 @@ typedef enum BACnetAuditOperation {
/* Enumerated values 0-31 are reserved for definition by ASHRAE.
Enumerated values 32-63 may be used by others subject to
the procedures and constraints described in Clause 23.
The enumerated values match the bit positions in
The enumerated values match the bit positions in
BACnetAuditOperationFlags. */
AUDIT_OPERATION_PROPRIETARY_MIN = 32,
AUDIT_OPERATION_PROPRIETARY_MAX = 63
+36 -26
View File
@@ -39,11 +39,14 @@
#include "bacnet/basic/object/bo.h"
#include "bacnet/basic/object/bv.h"
#include "bacnet/basic/object/calendar.h"
#include "bacnet/basic/object/channel.h"
#include "bacnet/basic/object/command.h"
#include "bacnet/basic/object/csv.h"
#include "bacnet/basic/object/iv.h"
#include "bacnet/basic/object/lc.h"
#if (BACNET_PROTOCOL_REVISION >= 14)
#include "bacnet/basic/object/channel.h"
#include "bacnet/basic/object/lo.h"
#endif
#include "bacnet/basic/object/lsp.h"
#include "bacnet/basic/object/lsz.h"
#include "bacnet/basic/object/ms-input.h"
@@ -55,6 +58,7 @@
#include "bacnet/basic/object/osv.h"
#include "bacnet/basic/object/piv.h"
#include "bacnet/basic/object/schedule.h"
#include "bacnet/basic/object/structured_view.h"
#include "bacnet/basic/object/time_value.h"
#include "bacnet/basic/object/trendlog.h"
#if defined(INTRINSIC_REPORTING)
@@ -78,6 +82,7 @@ extern bool Routed_Device_Write_Property_Local(
/* may be overridden by outside table */
static object_functions_t *Object_Table;
/* clang format off */
static object_functions_t My_Object_Table[] = {
{ OBJECT_DEVICE, NULL /* Init - don't init Device or it will recourse! */,
Device_Count, Device_Index_To_Instance,
@@ -111,7 +116,7 @@ static object_functions_t My_Object_Table[] = {
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 /* ReadRangeInfo */, NULL /* Iterator */,
Analog_Output_Encode_Value_List, Analog_Output_Change_Of_Value,
Analog_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
@@ -138,7 +143,7 @@ static object_functions_t My_Object_Table[] = {
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 /* ReadRangeInfo */, NULL /* Iterator */,
Binary_Output_Encode_Value_List, Binary_Output_Change_Of_Value,
Binary_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
@@ -147,7 +152,7 @@ static object_functions_t My_Object_Table[] = {
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 /* ReadRangeInfo */, NULL /* Iterator */,
Binary_Value_Encode_Value_List, Binary_Value_Change_Of_Value,
Binary_Value_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
@@ -227,7 +232,7 @@ static object_functions_t My_Object_Table[] = {
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 /* ReadRangeInfo */, NULL /* Iterator */,
Multistate_Input_Encode_Value_List, Multistate_Input_Change_Of_Value,
Multistate_Input_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
@@ -237,7 +242,7 @@ static object_functions_t My_Object_Table[] = {
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 /* Iterator */,
Multistate_Output_Encode_Value_List, Multistate_Output_Change_Of_Value,
Multistate_Output_Change_Of_Value_Clear, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
@@ -343,6 +348,14 @@ static object_functions_t My_Object_Table[] = {
NULL /* Intrinsic Reporting */, NULL /* Add_List_Element */,
NULL /* Remove_List_Element */, NULL /* Create */, NULL /* Delete */,
NULL /* Timer */ },
{ OBJECT_STRUCTURED_VIEW, Structured_View_Init, Structured_View_Count,
Structured_View_Index_To_Instance, Structured_View_Valid_Instance,
Structured_View_Object_Name, Structured_View_Read_Property,
NULL /* Write_Property */, Structured_View_Property_Lists,
NULL /* ReadRangeInfo */, NULL /* Iterator */, NULL /* Value_Lists */,
NULL /* COV */, NULL /* COV Clear */, NULL /* Intrinsic Reporting */,
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
Structured_View_Create, Structured_View_Delete, NULL /* Timer */ },
{ OBJECT_TIME_VALUE, Time_Value_Init, Time_Value_Count,
Time_Value_Index_To_Instance, Time_Value_Valid_Instance,
Time_Value_Object_Name, Time_Value_Read_Property,
@@ -368,6 +381,7 @@ static object_functions_t My_Object_Table[] = {
NULL /* Add_List_Element */, NULL /* Remove_List_Element */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
};
/* clang format on */
/** Glue function to let the Device object, when called by a handler,
* lookup which Object type needs to be invoked.
@@ -462,16 +476,19 @@ void Device_Objects_Property_List(BACNET_OBJECT_TYPE object_type,
return;
}
/* clang format off */
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Device_Properties_Required[] = { PROP_OBJECT_IDENTIFIER,
PROP_OBJECT_NAME, PROP_OBJECT_TYPE, PROP_SYSTEM_STATUS, PROP_VENDOR_NAME,
PROP_VENDOR_IDENTIFIER, PROP_MODEL_NAME, PROP_FIRMWARE_REVISION,
PROP_APPLICATION_SOFTWARE_VERSION, PROP_PROTOCOL_VERSION,
PROP_PROTOCOL_REVISION, PROP_PROTOCOL_SERVICES_SUPPORTED,
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, PROP_OBJECT_LIST,
PROP_MAX_APDU_LENGTH_ACCEPTED, PROP_SEGMENTATION_SUPPORTED,
PROP_APDU_TIMEOUT, PROP_NUMBER_OF_APDU_RETRIES, PROP_DEVICE_ADDRESS_BINDING,
PROP_DATABASE_REVISION, -1 };
static const int Device_Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_SYSTEM_STATUS, PROP_VENDOR_NAME, PROP_VENDOR_IDENTIFIER,
PROP_MODEL_NAME, PROP_FIRMWARE_REVISION, PROP_APPLICATION_SOFTWARE_VERSION,
PROP_PROTOCOL_VERSION, PROP_PROTOCOL_REVISION,
PROP_PROTOCOL_SERVICES_SUPPORTED, PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
PROP_OBJECT_LIST, PROP_MAX_APDU_LENGTH_ACCEPTED,
PROP_SEGMENTATION_SUPPORTED, PROP_APDU_TIMEOUT,
PROP_NUMBER_OF_APDU_RETRIES, PROP_DEVICE_ADDRESS_BINDING,
PROP_DATABASE_REVISION, -1
};
static const int Device_Properties_Optional[] = {
#if defined(BACDL_MSTP)
@@ -486,7 +503,10 @@ static const int Device_Properties_Optional[] = {
-1
};
static const int Device_Properties_Proprietary[] = { -1 };
static const int Device_Properties_Proprietary[] = {
-1
};
/* clang format on */
void Device_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
@@ -2231,16 +2251,6 @@ void Device_Init(object_functions_t *object_table)
}
pObject++;
}
/* create some dynamically created objects as examples */
if (!object_table) {
pObject = Object_Table;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if (pObject->Object_Create) {
pObject->Object_Create(BACNET_MAX_INSTANCE);
}
pObject++;
}
}
#if (BACNET_PROTOCOL_REVISION >= 14)
Channel_Write_Property_Internal_Callback_Set(Device_Write_Property);
#endif
+882
View File
@@ -0,0 +1,882 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @date May 2024
* @brief Structured View object is an object that provides a container
* to hold references to subordinate objects, which may include other
* Structured View objects, thereby allowing multilevel hierarchies
* to be created.
*
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacdcode.h"
#include "bacnet/bacerror.h"
#include "bacnet/bacapp.h"
#include "bacnet/bactext.h"
#include "bacnet/apdu.h"
#include "bacnet/npdu.h"
#include "bacnet/abort.h"
#include "bacnet/proplist.h"
#include "bacnet/reject.h"
#include "bacnet/rp.h"
#include "bacnet/basic/services.h"
#include "bacnet/basic/sys/keylist.h"
/* me! */
#include "structured_view.h"
struct object_data {
const char *Object_Name;
const char *Description;
BACNET_NODE_TYPE Node_Type;
const char *Node_Subtype;
BACNET_SUBORDINATE_DATA *Subordinate_List;
BACNET_RELATIONSHIP Default_Subordinate_Relationship;
BACNET_DEVICE_OBJECT_REFERENCE Represents;
};
/* Key List for storing the object data sorted by instance number */
static OS_Keylist Object_List;
/* clang-format off */
/* These three arrays are used by the ReadPropertyMultiple handler */
static const int Properties_Required[] = {
PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, PROP_OBJECT_TYPE,
PROP_NODE_TYPE, PROP_SUBORDINATE_LIST,
-1
};
static const int Properties_Optional[] = {
PROP_DESCRIPTION, PROP_NODE_SUBTYPE, PROP_SUBORDINATE_ANNOTATIONS,
PROP_SUBORDINATE_NODE_TYPES, PROP_SUBORDINATE_RELATIONSHIPS,
PROP_DEFAULT_SUBORDINATE_RELATIONSHIP,
PROP_REPRESENTS,
-1
};
static const int Properties_Proprietary[] = {
-1
};
/* standard properties that are arrays for this object,
but not necessary supported in this object */
static const int BACnetARRAY_Properties[] = {
PROP_SUBORDINATE_LIST, PROP_SUBORDINATE_ANNOTATIONS, PROP_SUBORDINATE_TAGS,
PROP_SUBORDINATE_NODE_TYPES, PROP_SUBORDINATE_RELATIONSHIPS, PROP_TAGS,
-1
};
/* clang-format on */
/**
* Returns the list of required, optional, and proprietary properties.
* Used by ReadPropertyMultiple service.
*
* @param pRequired - pointer to list of int terminated by -1, of
* BACnet required properties for this object.
* @param pOptional - pointer to list of int terminated by -1, of
* BACnet optkional properties for this object.
* @param pProprietary - pointer to list of int terminated by -1, of
* BACnet proprietary properties for this object.
*/
void Structured_View_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary)
{
if (pRequired) {
*pRequired = Properties_Required;
}
if (pOptional) {
*pOptional = Properties_Optional;
}
if (pProprietary) {
*pProprietary = Properties_Proprietary;
}
return;
}
/**
* Determines if a given Structured View instance is valid
*
* @param object_instance - object-instance number of the object
*
* @return true if the instance is valid, and false if not
*/
bool Structured_View_Valid_Instance(uint32_t object_instance)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
return true;
}
return false;
}
/**
* Determines the number of Structured View objects
*
* @return Number of Structured View objects
*/
unsigned Structured_View_Count(void)
{
return Keylist_Count(Object_List);
}
/**
* Determines the object instance-number for a given 0..(N-1) index
* of Structured View objects where N is Structured_View_Count().
*
* @param index - 0..(N-1) where N is Structured_View_Count()
*
* @return object instance-number for the given index
*/
uint32_t Structured_View_Index_To_Instance(unsigned index)
{
KEY key = UINT32_MAX;
Keylist_Index_Key(Object_List, index, &key);
return key;
}
/**
* For a given object instance-number, determines a 0..(N-1) index
* of Structured View objects where N is Structured_View_Count().
*
* @param object_instance - object-instance number of the object
*
* @return index for the given instance-number, or Structured_View_Count()
* if not valid.
*/
unsigned Structured_View_Instance_To_Index(uint32_t object_instance)
{
return Keylist_Index(Object_List, object_instance);
}
/**
* For a given object instance-number, loads the object-name into
* a characterstring. Note that the object name must be unique
* within this device.
*
* @param object_instance - object-instance number of the object
* @param object_name - holds the object-name retrieved
*
* @return true if object-name was retrieved
*/
bool Structured_View_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name)
{
bool status = false;
struct object_data *pObject;
char name_text[24] = "Structured-View-4194303";
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Object_Name) {
status =
characterstring_init_ansi(object_name, pObject->Object_Name);
} else {
snprintf(
name_text, sizeof(name_text), "Structured-View-%u",
object_instance);
status = characterstring_init_ansi(object_name, name_text);
}
}
return status;
}
/**
* For a given object instance-number, sets the object-name
* Note that the object name must be unique within this device.
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the object-name to be set
*
* @return true if object-name was set
*/
bool Structured_View_Name_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject && new_name) {
status = true;
pObject->Object_Name = new_name;
}
return status;
}
/**
* For a given object instance-number, returns the description
*
* @param object_instance - object-instance number of the object
*
* @return description text or NULL if not found
*/
char *Structured_View_Description(uint32_t object_instance)
{
char *name = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = (char *)pObject->Description;
} else {
name = "";
}
}
return name;
}
/**
* For a given object instance-number, sets the description
*
* @param object_instance - object-instance number of the object
* @param new_name - holds the description to be set
*
* @return true if object-name was set
*/
bool Structured_View_Description_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Description = new_name;
}
return status;
}
/**
* @brief For a given object instance-number, returns the Node_Type
* @param object_instance - object-instance number of the object
* @return Node_Type or BACNET_NODE_UNKNOWN if not found
*/
BACNET_NODE_TYPE Structured_View_Node_Type(uint32_t object_instance)
{
BACNET_NODE_TYPE node_type = BACNET_NODE_UNKNOWN;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
node_type = pObject->Node_Type;
}
return node_type;
}
/**
* @brief For a given object instance-number, sets the Node_Type
* @param object_instance - object-instance number of the object
* @param node_type - holds the Node_Type to be set
* @return true if Node_Type was set
*/
bool Structured_View_Node_Type_Set(
uint32_t object_instance, BACNET_NODE_TYPE node_type)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Node_Type = node_type;
}
return status;
}
/**
* @brief For a given object instance-number, returns the Node_Subtype
* @param object_instance - object-instance number of the object
* @return Node_Subtype text or NULL if not found
*/
char *Structured_View_Node_Subtype(uint32_t object_instance)
{
char *name = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
if (pObject->Description) {
name = (char *)pObject->Node_Subtype;
} else {
name = "";
}
}
return name;
}
/**
* @brief For a given object instance-number, sets the Node_Subtype
* @param object_instance - object-instance number of the object
* @param new_name - holds the Node_Subtype to be set
* @return true if Node_Subtype was set
*/
bool Structured_View_Node_Subtype_Set(uint32_t object_instance, char *new_name)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Description = new_name;
}
return status;
}
/**
* @brief For a given object instance-number, returns the Subordinate_List
* @param object_instance - object-instance number of the object
* @return Subordinate_List or NULL if not found
*/
BACNET_SUBORDINATE_DATA *
Structured_View_Subordinate_List(uint32_t object_instance)
{
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
subordinate_list = pObject->Subordinate_List;
}
return subordinate_list;
}
/**
* @brief For a given object instance-number, sets the Subordinate_List
* @param object_instance - object-instance number of the object
* @param subordinate_list - holds the Subordinate_List to be set
*/
void Structured_View_Subordinate_List_Set(
uint32_t object_instance, BACNET_SUBORDINATE_DATA *subordinate_list)
{
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
pObject->Subordinate_List = subordinate_list;
}
}
/**
* @brief For a given object instance-number, returns the
* Default_Subordinate_Relationship
* @param object_instance - object-instance number of the object
* @return Default_Subordinate_Relationship or BACNET_RELATIONSHIP_DEFAULT if
* not found
*/
BACNET_RELATIONSHIP
Structured_View_Default_Subordinate_Relationship(uint32_t object_instance)
{
BACNET_RELATIONSHIP relationship = BACNET_RELATIONSHIP_DEFAULT;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
relationship = pObject->Default_Subordinate_Relationship;
}
return relationship;
}
/**
* @brief For a given object instance-number, sets the
* Default_Subordinate_Relationship
* @param object_instance - object-instance number of the object
* @param relationship - holds the Default_Subordinate_Relationship to be set
* @return true if Default_Subordinate_Relationship was set
*/
bool Structured_View_Default_Subordinate_Relationship_Set(
uint32_t object_instance, BACNET_RELATIONSHIP relationship)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Default_Subordinate_Relationship = relationship;
}
return status;
}
/**
* @brief For a given object instance-number, returns the Represents
* property value
* @param object_instance - object-instance number of the object
* @return Represents or NULL if not found
*/
BACNET_DEVICE_OBJECT_REFERENCE *
Structured_View_Represents(uint32_t object_instance)
{
BACNET_DEVICE_OBJECT_REFERENCE *represents = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
represents = &pObject->Represents;
}
return represents;
}
/**
* @brief For a given object instance-number, sets the Represents
* property value
* @param object_instance - object-instance number of the object
* @param represents - holds the Represents to be set
* @return true if Represents was set
*/
bool Structured_View_Represents_Set(
uint32_t object_instance, BACNET_DEVICE_OBJECT_REFERENCE *represents)
{
bool status = false; /* return value */
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
status = true;
pObject->Represents = *represents;
}
return status;
}
/**
* @brief For a given object instance-number, returns the number of
* Subordinate_List elements
* @param object_instance - object-instance number of the object
* @return number of Subordinate_List elements
*/
unsigned int Structured_View_Subordinate_List_Count(uint32_t object_instance)
{
unsigned int count = 0;
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
subordinate_list = pObject->Subordinate_List;
while (subordinate_list) {
count++;
subordinate_list = subordinate_list->next;
}
}
return count;
}
/**
* @brief For a given object instance-number, returns the number of
* Subordinate_List elements
* @param object_instance - object-instance number of the object
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @return Subordinate_List element or NULL if not found
*/
BACNET_SUBORDINATE_DATA *Structured_View_Subordinate_List_Member(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index)
{
BACNET_ARRAY_INDEX index = 0;
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
struct object_data *pObject;
pObject = Keylist_Data(Object_List, object_instance);
if (pObject) {
subordinate_list = pObject->Subordinate_List;
while (subordinate_list) {
if (index == array_index) {
break;
}
index++;
subordinate_list = subordinate_list->next;
}
}
return subordinate_list;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Structured_View_Subordinate_List_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_DEVICE_OBJECT_REFERENCE value = { 0 };
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
subordinate_list =
Structured_View_Subordinate_List_Member(object_instance, array_index);
if (subordinate_list) {
/* BACnetDeviceObjectReference */
value.deviceIdentifier.type = OBJECT_DEVICE;
value.deviceIdentifier.instance = subordinate_list->Device_Instance;
value.objectIdentifier.type = subordinate_list->Object_Type;
value.objectIdentifier.instance = subordinate_list->Object_Instance;
apdu_len = bacapp_encode_device_obj_ref(apdu, &value);
}
return apdu_len;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Structured_View_Subordinate_Annotations_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_CHARACTER_STRING value = { 0 };
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
subordinate_list =
Structured_View_Subordinate_List_Member(object_instance, array_index);
if (subordinate_list) {
/* BACnetCharacterString */
characterstring_init_ansi(&value, subordinate_list->Annotations);
apdu_len = encode_application_character_string(apdu, &value);
}
return apdu_len;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Structured_View_Subordinate_Node_Types_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
subordinate_list =
Structured_View_Subordinate_List_Member(object_instance, array_index);
if (subordinate_list) {
/* BACnetNodeType */
apdu_len =
encode_application_enumerated(apdu, subordinate_list->Node_Type);
}
return apdu_len;
}
/**
* @brief Encode a BACnetARRAY property element
* @param object_instance [in] BACnet network port object instance number
* @param array_index [in] array index requested:
* 0 to N for individual array members
* @param apdu [out] Buffer in which the APDU contents are built, or NULL to
* return the length of buffer if it had been built
* @return The length of the apdu encoded or
* BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
*/
int Structured_View_Subordinate_Relationships_Element_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_SUBORDINATE_DATA *subordinate_list = NULL;
subordinate_list =
Structured_View_Subordinate_List_Member(object_instance, array_index);
if (subordinate_list) {
/* BACnetRelationship */
apdu_len =
encode_application_enumerated(apdu, subordinate_list->Relationship);
}
return apdu_len;
}
/**
* @brief Determine if the object property is a BACnetARRAY property
* @param object_property - object-property to be checked
* @return true if the property is a BACnetARRAY property
*/
static bool BACnetARRAY_Property(int object_property)
{
return property_list_member(BACnetARRAY_Properties, object_property);
}
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
*
* @param rpdata - BACNET_READ_PROPERTY_DATA data, including
* requested data and space for the reply, or error response.
*
* @return number of APDU bytes in the response, or
* BACNET_STATUS_ERROR on error.
*/
int Structured_View_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = 0; /* return value */
BACNET_CHARACTER_STRING char_string;
uint32_t count = 0;
uint8_t *apdu = NULL;
uint16_t apdu_max = 0;
if ((rpdata == NULL) || (rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
apdu_max = rpdata->application_data_len;
switch (rpdata->object_property) {
case PROP_OBJECT_IDENTIFIER:
apdu_len = encode_application_object_id(
&apdu[0], rpdata->object_type, rpdata->object_instance);
break;
case PROP_OBJECT_NAME:
Structured_View_Object_Name(rpdata->object_instance, &char_string);
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
break;
case PROP_OBJECT_TYPE:
apdu_len =
encode_application_enumerated(&apdu[0], rpdata->object_type);
break;
case PROP_NODE_TYPE:
apdu_len = encode_application_enumerated(
&apdu[0], Structured_View_Node_Type(rpdata->object_instance));
break;
case PROP_SUBORDINATE_LIST:
count =
Structured_View_Subordinate_List_Count(rpdata->object_instance);
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Structured_View_Subordinate_List_Element_Encode, count, apdu,
apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_DESCRIPTION:
characterstring_init_ansi(
&char_string,
Structured_View_Description(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
case PROP_NODE_SUBTYPE:
characterstring_init_ansi(
&char_string,
Structured_View_Node_Subtype(rpdata->object_instance));
apdu_len = encode_application_character_string(apdu, &char_string);
break;
case PROP_SUBORDINATE_ANNOTATIONS:
count =
Structured_View_Subordinate_List_Count(rpdata->object_instance);
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Structured_View_Subordinate_Annotations_Element_Encode, count,
apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_SUBORDINATE_NODE_TYPES:
count =
Structured_View_Subordinate_List_Count(rpdata->object_instance);
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Structured_View_Subordinate_Node_Types_Element_Encode, count,
apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_SUBORDINATE_RELATIONSHIPS:
count =
Structured_View_Subordinate_List_Count(rpdata->object_instance);
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Structured_View_Subordinate_Relationships_Element_Encode, count,
apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_DEFAULT_SUBORDINATE_RELATIONSHIP:
apdu_len = encode_application_enumerated(
&apdu[0],
Structured_View_Default_Subordinate_Relationship(
rpdata->object_instance));
break;
case PROP_REPRESENTS:
apdu_len = bacapp_encode_device_obj_ref(
&apdu[0], Structured_View_Represents(rpdata->object_instance));
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
apdu_len = BACNET_STATUS_ERROR;
break;
}
/* only array properties can have array options */
if ((apdu_len >= 0) && (!BACnetARRAY_Property(rpdata->object_property)) &&
(rpdata->array_index != BACNET_ARRAY_ALL)) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY;
apdu_len = BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* Creates a Structured View object
* @param object_instance - object-instance number of the object
* @return object_instance if the object is created, else BACNET_MAX_INSTANCE
*/
uint32_t Structured_View_Create(uint32_t object_instance)
{
struct object_data *pObject = NULL;
int index = 0;
if (object_instance > BACNET_MAX_INSTANCE) {
return BACNET_MAX_INSTANCE;
} else if (object_instance == BACNET_MAX_INSTANCE) {
/* wildcard instance */
/* the Object_Identifier property of the newly created object
shall be initialized to a value that is unique within the
responding BACnet-user device. The method used to generate
the object identifier is a local matter.*/
object_instance = Keylist_Next_Empty_Key(Object_List, 1);
}
pObject = Keylist_Data(Object_List, object_instance);
if (!pObject) {
pObject = calloc(1, sizeof(struct object_data));
if (!pObject) {
return BACNET_MAX_INSTANCE;
}
pObject->Object_Name = NULL;
pObject->Description = NULL;
pObject->Node_Subtype = NULL;
pObject->Subordinate_List = NULL;
pObject->Default_Subordinate_Relationship = BACNET_RELATIONSHIP_DEFAULT;
pObject->Represents.deviceIdentifier.type = OBJECT_NONE;
pObject->Represents.deviceIdentifier.instance = BACNET_MAX_INSTANCE;
pObject->Represents.objectIdentifier.type = OBJECT_DEVICE;
pObject->Represents.objectIdentifier.instance = BACNET_MAX_INSTANCE;
pObject->Node_Type = BACNET_NODE_UNKNOWN;
/* add to list */
index = Keylist_Data_Add(Object_List, object_instance, pObject);
if (index < 0) {
free(pObject);
return BACNET_MAX_INSTANCE;
}
}
return object_instance;
}
/**
* Deletes an Structured View object
* @param object_instance - object-instance number of the object
* @return true if the object is deleted
*/
bool Structured_View_Delete(uint32_t object_instance)
{
bool status = false;
struct object_data *pObject = NULL;
pObject = Keylist_Data_Delete(Object_List, object_instance);
if (pObject) {
free(pObject);
status = true;
}
return status;
}
/**
* Deletes all the Time Values and their data
*/
void Structured_View_Cleanup(void)
{
struct object_data *pObject;
if (Object_List) {
do {
pObject = Keylist_Data_Pop(Object_List);
if (pObject) {
free(pObject);
}
} while (pObject);
Keylist_Delete(Object_List);
Object_List = NULL;
}
}
/**
* Initializes the Structured View object data
*/
void Structured_View_Init(void)
{
Object_List = Keylist_Create();
if (Object_List) {
atexit(Structured_View_Cleanup);
}
}
+110
View File
@@ -0,0 +1,110 @@
/**
* @file
* @author Steve Karg <skarg@users.sourceforge.net>
* @date May 2024
* @brief API for a Structured View object used by a BACnet device object
* @section LICENSE
*
* SPDX-License-Identifier: MIT
*/
#ifndef BACNET_STRUCTURED_VIEW_OBJECT_H
#define BACNET_STRUCTURED_VIEW_OBJECT_H
#include <stdbool.h>
#include <stdint.h>
/* BACnet Stack defines - first */
#include "bacnet/bacdef.h"
/* BACnet Stack API */
#include "bacnet/bacerror.h"
#include "bacnet/bacstr.h"
#include "bacnet/bacdevobjpropref.h"
#include "bacnet/rp.h"
struct BACnetSubordinateData;
typedef struct BACnetSubordinateData {
uint32_t Device_Instance;
BACNET_OBJECT_TYPE Object_Type;
uint32_t Object_Instance;
const char *Annotations;
BACNET_NODE_TYPE Node_Type;
BACNET_RELATIONSHIP Relationship;
/* simple linked list */
struct BACnetSubordinateData *next;
} BACNET_SUBORDINATE_DATA;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
BACNET_STACK_EXPORT
void Structured_View_Property_Lists(
const int **pRequired, const int **pOptional, const int **pProprietary);
BACNET_STACK_EXPORT
bool Structured_View_Valid_Instance(uint32_t object_instance);
BACNET_STACK_EXPORT
unsigned Structured_View_Count(void);
BACNET_STACK_EXPORT
uint32_t Structured_View_Index_To_Instance(unsigned index);
BACNET_STACK_EXPORT
unsigned Structured_View_Instance_To_Index(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Object_Name(
uint32_t object_instance, BACNET_CHARACTER_STRING *object_name);
BACNET_STACK_EXPORT
bool Structured_View_Name_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
int Structured_View_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata);
BACNET_STACK_EXPORT
char *Structured_View_Description(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Description_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
BACNET_NODE_TYPE Structured_View_Node_Type(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Node_Type_Set(
uint32_t object_instance, BACNET_NODE_TYPE node_type);
BACNET_STACK_EXPORT
char *Structured_View_Node_Subtype(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Node_Subtype_Set(uint32_t object_instance, char *new_name);
BACNET_STACK_EXPORT
BACNET_SUBORDINATE_DATA *
Structured_View_Subordinate_List(uint32_t object_instance);
BACNET_STACK_EXPORT
void Structured_View_Subordinate_List_Set(
uint32_t object_instance, BACNET_SUBORDINATE_DATA *subordinate_list);
BACNET_STACK_EXPORT
BACNET_RELATIONSHIP
Structured_View_Default_Subordinate_Relationship(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Default_Subordinate_Relationship_Set(
uint32_t object_instance, BACNET_RELATIONSHIP relationship);
BACNET_STACK_EXPORT
BACNET_DEVICE_OBJECT_REFERENCE *
Structured_View_Represents(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Represents_Set(
uint32_t object_instance, BACNET_DEVICE_OBJECT_REFERENCE *represents);
BACNET_STACK_EXPORT
uint32_t Structured_View_Create(uint32_t object_instance);
BACNET_STACK_EXPORT
bool Structured_View_Delete(uint32_t object_instance);
BACNET_STACK_EXPORT
void Structured_View_Cleanup(void);
BACNET_STACK_EXPORT
void Structured_View_Init(void);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
+1
View File
@@ -140,6 +140,7 @@ list(APPEND testdirs
bacnet/basic/object/osv
bacnet/basic/object/piv
bacnet/basic/object/schedule
bacnet/basic/object/structured_view
bacnet/basic/object/time_value
bacnet/basic/object/trendlog
# basic/sys
@@ -73,6 +73,7 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/basic/object/osv.c
${SRC_DIR}/bacnet/basic/object/piv.c
${SRC_DIR}/bacnet/basic/object/schedule.c
${SRC_DIR}/bacnet/basic/object/structured_view.c
${SRC_DIR}/bacnet/basic/object/time_value.c
${SRC_DIR}/bacnet/basic/object/trendlog.c
${SRC_DIR}/bacnet/basic/service/h_apdu.c
+4 -2
View File
@@ -221,6 +221,8 @@ void bacnet_object_properties_read_write_test(
zassert_equal(len, BACNET_STATUS_ERROR, NULL);
wpdata.object_property = PROP_ALL;
wpdata.array_index = BACNET_ARRAY_ALL;
status = write_property(&wpdata);
zassert_false(status, NULL);
if (write_property) {
status = write_property(&wpdata);
zassert_false(status, NULL);
}
}
@@ -0,0 +1,69 @@
# SPDX-License-Identifier: MIT
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(test_${basename}
VERSION 1.0.0
LANGUAGES C)
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/src"
SRC_DIR
${CMAKE_CURRENT_SOURCE_DIR})
string(REGEX REPLACE
"/test/bacnet/[a-zA-Z_/-]*$"
"/test"
TST_DIR
${CMAKE_CURRENT_SOURCE_DIR})
set(ZTST_DIR "${TST_DIR}/ztest/src")
add_compile_definitions(
BIG_ENDIAN=0
CONFIG_ZTEST=1
)
include_directories(
${SRC_DIR}
${TST_DIR}/bacnet/basic/object
${TST_DIR}/ztest/include
)
add_executable(${PROJECT_NAME}
# File(s) under test
${SRC_DIR}/bacnet/basic/object/structured_view.c
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacapp.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacdest.c
${SRC_DIR}/bacnet/bacdevobjpropref.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/datetime.c
${SRC_DIR}/bacnet/basic/sys/days.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/hostnport.c
${SRC_DIR}/bacnet/lighting.c
${SRC_DIR}/bacnet/proplist.c
${SRC_DIR}/bacnet/timestamp.c
${SRC_DIR}/bacnet/wp.c
${SRC_DIR}/bacnet/weeklyschedule.c
${SRC_DIR}/bacnet/bactimevalue.c
${SRC_DIR}/bacnet/dailyschedule.c
${SRC_DIR}/bacnet/calendar_entry.c
${SRC_DIR}/bacnet/special_event.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
${SRC_DIR}/bacnet/basic/sys/keylist.c
# Test and test library files
./src/main.c
${TST_DIR}/bacnet/basic/object/property_test.c
${ZTST_DIR}/ztest_mock.c
${ZTST_DIR}/ztest.c
)
@@ -0,0 +1,59 @@
/**
* @file
* @brief Unit test for object
* @author Steve Karg <skarg@users.sourceforge.net>
* @date May 2024
*
* SPDX-License-Identifier: MIT
*/
#include <zephyr/ztest.h>
#include <bacnet/basic/object/structured_view.h>
#include <property_test.h>
/**
* @addtogroup bacnet_tests
* @{
*/
/**
* @brief Test
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(tests_object_structured_view, test_object_structured_view)
#else
static void test_object_structured_view(void)
#endif
{
bool status = false;
unsigned count = 0, index = 0;
const uint32_t instance = 123;
const int skip_fail_property_list[] = { -1 };
Structured_View_Init();
Structured_View_Create(instance);
status = Structured_View_Valid_Instance(instance);
zassert_true(status, NULL);
index = Structured_View_Instance_To_Index(instance);
zassert_equal(index, 0, NULL);
count = Structured_View_Count();
zassert_true(count > 0, NULL);
bacnet_object_properties_read_write_test(
OBJECT_STRUCTURED_VIEW, instance, Structured_View_Property_Lists,
Structured_View_Read_Property, NULL, skip_fail_property_list);
}
/**
* @}
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(tests_object_structured_view, NULL, NULL, NULL, NULL, NULL);
#else
void test_main(void)
{
ztest_test_suite(
tests_object_structured_view,
ztest_unit_test(test_object_structured_view));
ztest_run_test_suite(tests_object_structured_view);
}
#endif
+1
View File
@@ -372,6 +372,7 @@ set(BACNETSTACK_BASIC_SRCS
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_OCTET_STRING_VALUE}>:${BACNETSTACK_SRC}/bacnet/basic/object/osv.c>
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_POSITIVE_INTEGER_VALUE}>:${BACNETSTACK_SRC}/bacnet/basic/object/piv.c>
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_SCHEDULE}>:${BACNETSTACK_SRC}/bacnet/basic/object/schedule.c>
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_STRUCTURED_VIEW}>:${BACNETSTACK_SRC}/bacnet/basic/object/structured_view.c>
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_TIME_VALUE}>:${BACNETSTACK_SRC}/bacnet/basic/object/time_value.c>
$<$<BOOL:${CONFIG_BACNET_BASIC_OBJECT_TRENDLOG}>:${BACNETSTACK_SRC}/bacnet/basic/object/trendlog.c>
${BACNETSTACK_SRC}/bacnet/basic/service/h_alarm_ack.c
+6
View File
@@ -342,6 +342,12 @@ config BACNET_BASIC_OBJECT_SCHEDULE
help
Use the BACnet basic schedule object
config BACNET_BASIC_OBJECT_STRUCTURED_VIEW
bool "Use the BACnet basic structured view object"
default false
help
Use the BACnet basic structured view object
config BACNET_BASIC_OBJECT_TIME_VALUE
bool "Use the BACnet basic time value object"
default false