Feature/raspberry pi blinkt color objects (#503)

* fixed BACnetXYcolor and BACnetColorCommand encode and decoding and improved unit test coverage. Refactored BACnetXYcolor to/from ascii into lighting module.

* added to the color, color temperature, and lighting output objects a fade/ramp/step engine.  Added color and color command coercion into the channel object and enabled color temperature object coercion.  Added CreateObject and DeleteObject service handling to the color, color temperature, lighting output, and channel objects.

* added blinkt demo app for Raspberry Pi [WIP]

* updated gitignore to simplify handling of apps folder contents

* fixed piface demo build

* added RaspiOS to pipeline for piface and blinkt! demo builds

* added device object timer function for child object types into example Device object.  Refactored device object to increment database revision for create or delete object services.   Refactored example app/server to use mstimer library and device child object timers.

---------

Co-authored-by: Steve Karg <skarg@users.sourceforge.net>
This commit is contained in:
Steve Karg
2023-09-28 10:50:32 -05:00
committed by GitHub
parent b0749572f6
commit da2dc9841a
64 changed files with 8012 additions and 1637 deletions
+3 -1
View File
@@ -7,7 +7,7 @@ TARGET = bacpiface
# BACnet objects that are used with this app
BACNET_OBJECT_DIR = $(BACNET_SRC_DIR)/bacnet/basic/object
SRC = main.c \
$(BACNET_OBJECT_DIR)/device.c \
device.c \
$(BACNET_OBJECT_DIR)/netport.c \
$(BACNET_OBJECT_DIR)/bi.c \
$(BACNET_OBJECT_DIR)/bo.c
@@ -24,6 +24,8 @@ PIFACE_INCLUDE = libpifacedigital/src
PIFACE_LIB = libpifacedigital
MCP23S17_LIB = libmcp23s17
CFLAGS += -I$(PIFACE_INCLUDE)
LDPIFACE = -Wl,-L$(PIFACE_LIB),-lpifacedigital,-L$(MCP23S17_LIB),-lmcp23s17
LFLAGS += $(LDPIFACE)
+468 -154
View File
@@ -1,6 +1,6 @@
/**************************************************************************
*
* Copyright (C) 2014 Steve Karg <skarg@users.sourceforge.net>
* Copyright (C) 2005,2006,2009 Steve Karg <skarg@users.sourceforge.net>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -22,15 +22,19 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*********************************************************************/
/** @file device.c Base "class" for handling all BACnet objects belonging
* to a BACnet device, as well as Device-specific properties. */
#include <stdbool.h>
#include <stdint.h>
#include <string.h> /* for memmove */
#include <time.h> /* for timezone, localtime */
#include "bacnet/bacdef.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacenum.h"
#include "bacnet/bacapp.h"
#include "bacnet/config.h" /* the custom stuff */
#include "bacnet/datetime.h"
#include "bacnet/apdu.h"
#include "bacnet/wp.h" /* WriteProperty handling */
#include "bacnet/rp.h" /* ReadProperty handling */
@@ -41,8 +45,6 @@
#include "bacnet/datalink/datalink.h"
#include "bacnet/basic/binding/address.h"
/* include the OS specific */
#include "bacnet/basic/sys/mstimer.h"
/* include the device object */
#include "bacnet/basic/object/device.h"
#include "bacnet/basic/object/bi.h"
#include "bacnet/basic/object/bo.h"
@@ -67,14 +69,18 @@ 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 */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
#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 */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
#endif
{ OBJECT_BINARY_INPUT, Binary_Input_Init, Binary_Input_Count,
Binary_Input_Index_To_Instance, Binary_Input_Valid_Instance,
@@ -82,20 +88,25 @@ static object_functions_t My_Object_Table[] = {
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 */,
NULL /* Create */, NULL /* Delete */, NULL /* Timer */ },
{ 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 */,
Binary_Output_Create, Binary_Output_Delete, NULL /* Timer */},
{ 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 */,
NULL /* Create */, NULL /* Delete */ , NULL /* Timer */}
};
/** Glue function to let the Device object, when called by a handler,
@@ -208,6 +219,10 @@ static const int Device_Properties_Optional[] = {
#endif
PROP_DESCRIPTION, PROP_LOCAL_TIME, PROP_UTC_OFFSET, PROP_LOCAL_DATE,
PROP_DAYLIGHT_SAVINGS_STATUS, PROP_LOCATION, PROP_ACTIVE_COV_SUBSCRIPTIONS,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS, PROP_TIME_SYNCHRONIZATION_INTERVAL,
PROP_ALIGN_INTERVALS, PROP_INTERVAL_OFFSET,
#endif
-1
};
@@ -259,10 +274,15 @@ static BACNET_DATE Local_Date; /* rely on OS, if there is one */
If your UTC offset is -5hours of GMT,
then BACnet UTC offset is +5hours.
BACnet UTC offset is expressed in minutes. */
static int32_t UTC_Offset = 5 * 60;
static int16_t UTC_Offset = 5 * 60;
static bool Daylight_Savings_Status = false; /* rely on OS */
/* List_Of_Session_Keys */
#if defined(BACNET_TIME_MASTER)
static bool Align_Intervals;
static uint32_t Interval_Minutes;
static uint32_t Interval_Offset_Minutes;
/* Time_Synchronization_Recipients */
#endif
/* List_Of_Session_Keys */
/* Max_Master - rely on MS/TP subsystem, if there is one */
/* Max_Info_Frames - rely on MS/TP subsystem, if there is one */
/* Device_Address_Binding - required, but relies on binding cache */
@@ -296,8 +316,18 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
{
bool status = false;
/* Note: you could use a mix of state and password to multiple things */
if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
20 characters. For those devices that require the password as a
protection, the service request shall be denied if the parameter
is absent or if the password is incorrect. For those devices that
do not require a password, this parameter shall be ignored.*/
if (characterstring_length(&rd_data->password) > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_ansi_same(&rd_data->password, Reinit_Password)) {
/* Note: you could use a mix of state and password to
accomplish multiple things before restarting */
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
@@ -347,7 +377,7 @@ unsigned Device_Count(void)
uint32_t Device_Index_To_Instance(unsigned index)
{
index = index;
(void)index;
return Object_Instance_Number;
}
@@ -361,11 +391,7 @@ uint32_t Device_Index_To_Instance(unsigned index)
*/
uint32_t Device_Object_Instance_Number(void)
{
#ifdef BAC_ROUTING
return Routed_Device_Object_Instance_Number();
#else
return Object_Instance_Number;
#endif
}
bool Device_Set_Object_Instance_Number(uint32_t object_id)
@@ -413,6 +439,11 @@ bool Device_Set_Object_Name(BACNET_CHARACTER_STRING *object_name)
return status;
}
bool Device_Object_Name_ANSI_Init(const char *value)
{
return characterstring_init_ansi(&My_Object_Name, value);
}
BACNET_DEVICE_STATUS Device_System_Status(void)
{
return System_Status;
@@ -813,57 +844,8 @@ bool Device_Object_Name_Copy(BACNET_OBJECT_TYPE object_type,
static void Update_Current_Time(void)
{
struct tm *tblock = NULL;
#if defined(_MSC_VER)
time_t tTemp;
#else
struct timeval tv;
#endif
/*
struct tm
int tm_sec Seconds [0,60].
int tm_min Minutes [0,59].
int tm_hour Hour [0,23].
int tm_mday Day of month [1,31].
int tm_mon Month of year [0,11].
int tm_year Years since 1900.
int tm_wday Day of week [0,6] (Sunday =0).
int tm_yday Day of year [0,365].
int tm_isdst Daylight Savings flag.
*/
#if defined(_MSC_VER)
time(&tTemp);
tblock = (struct tm *)localtime(&tTemp);
#else
if (gettimeofday(&tv, NULL) == 0) {
tblock = (struct tm *)localtime((const time_t *)&tv.tv_sec);
}
#endif
if (tblock) {
datetime_set_date(&Local_Date, (uint16_t)tblock->tm_year + 1900,
(uint8_t)tblock->tm_mon + 1, (uint8_t)tblock->tm_mday);
#if !defined(_MSC_VER)
datetime_set_time(&Local_Time, (uint8_t)tblock->tm_hour,
(uint8_t)tblock->tm_min, (uint8_t)tblock->tm_sec,
(uint8_t)(tv.tv_usec / 10000));
#else
datetime_set_time(&Local_Time, (uint8_t)tblock->tm_hour,
(uint8_t)tblock->tm_min, (uint8_t)tblock->tm_sec, 0);
#endif
if (tblock->tm_isdst) {
Daylight_Savings_Status = true;
} else {
Daylight_Savings_Status = false;
}
/* note: timezone is declared in <time.h> stdlib. */
UTC_Offset = timezone / 60;
} else {
datetime_date_wildcard_set(&Local_Date);
datetime_time_wildcard_set(&Local_Time);
Daylight_Savings_Status = false;
}
datetime_local(
&Local_Date, &Local_Time, &UTC_Offset, &Daylight_Savings_Status);
}
void Device_getCurrentDateTime(BACNET_DATE_TIME *DateTime)
@@ -881,11 +863,90 @@ int32_t Device_UTC_Offset(void)
return UTC_Offset;
}
void Device_UTC_Offset_Set(int16_t offset)
{
UTC_Offset = offset;
}
bool Device_Daylight_Savings_Status(void)
{
return Daylight_Savings_Status;
}
#if defined(BACNET_TIME_MASTER)
/**
* Sets the time sync interval in minutes
*
* @param flag
* This property, of type BOOLEAN, specifies whether (TRUE)
* or not (FALSE) clock-aligned periodic time synchronization is
* enabled. If periodic time synchronization is enabled and the
* time synchronization interval is a factor of (divides without
* remainder) an hour or day, then the beginning of the period
* specified for time synchronization shall be aligned to the hour or
* day, respectively. If this property is present, it shall be writable.
*/
bool Device_Align_Intervals_Set(bool flag)
{
Align_Intervals = flag;
return true;
}
bool Device_Align_Intervals(void)
{
return Align_Intervals;
}
/**
* Sets the time sync interval in minutes
*
* @param minutes
* This property, of type Unsigned, specifies the periodic
* interval in minutes at which TimeSynchronization and
* UTCTimeSynchronization requests shall be sent. If this
* property has a value of zero, then periodic time synchronization is
* disabled. If this property is present, it shall be writable.
*/
bool Device_Time_Sync_Interval_Set(uint32_t minutes)
{
Interval_Minutes = minutes;
return true;
}
uint32_t Device_Time_Sync_Interval(void)
{
return Interval_Minutes;
}
/**
* Sets the time sync interval offset value.
*
* @param minutes
* This property, of type Unsigned, specifies the offset in
* minutes from the beginning of the period specified for time
* synchronization until the actual time synchronization requests
* are sent. The offset used shall be the value of Interval_Offset
* modulo the value of Time_Synchronization_Interval;
* e.g., if Interval_Offset has the value 31 and
* Time_Synchronization_Interval is 30, the offset used shall be 1.
* Interval_Offset shall have no effect if Align_Intervals is
* FALSE. If this property is present, it shall be writable.
*/
bool Device_Interval_Offset_Set(uint32_t minutes)
{
Interval_Offset_Minutes = minutes;
return true;
}
uint32_t Device_Interval_Offset(void)
{
return Interval_Offset_Minutes;
}
#endif
/* return the length of the apdu encoded or BACNET_STATUS_ERROR for error or
BACNET_STATUS_ABORT for abort message */
int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
@@ -1002,7 +1063,8 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
pObject = Object_Table;
while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) {
if ((pObject->Object_Count) && (pObject->Object_Count() > 0)) {
bitstring_set_bit(&bit_string, pObject->Object_Type, true);
bitstring_set_bit(
&bit_string, (uint8_t)pObject->Object_Type, true);
}
pObject++;
}
@@ -1050,6 +1112,28 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len =
encode_application_unsigned(&apdu[0], dlmstp_max_master());
break;
#endif
#if defined(BACNET_TIME_MASTER)
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
apdu_len = handler_timesync_encode_recipients(&apdu[0], MAX_APDU);
if (apdu_len < 0) {
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
apdu_len = BACNET_STATUS_ABORT;
}
break;
case PROP_TIME_SYNCHRONIZATION_INTERVAL:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Time_Sync_Interval());
break;
case PROP_ALIGN_INTERVALS:
apdu_len =
encode_application_boolean(&apdu[0], Device_Align_Intervals());
break;
case PROP_INTERVAL_OFFSET:
apdu_len =
encode_application_unsigned(&apdu[0], Device_Interval_Offset());
break;
#endif
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
apdu_len = handler_cov_encode_subscriptions(&apdu[0], apdu_max);
@@ -1071,6 +1155,59 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
return apdu_len;
}
/** Looks up the common Object and Property, and encodes its Value in an
* APDU. Sets the error class and code if request is not appropriate.
* @param pObject - object table
* @param rpdata [in,out] Structure with the requested Object & Property info
* on entry, and APDU message on return.
* @return The length of the APDU on success, else BACNET_STATUS_ERROR
*/
static int Read_Property_Common(
struct object_functions *pObject, BACNET_READ_PROPERTY_DATA *rpdata)
{
int apdu_len = BACNET_STATUS_ERROR;
BACNET_CHARACTER_STRING char_string;
uint8_t *apdu = NULL;
#if (BACNET_PROTOCOL_REVISION >= 14)
struct special_property_list_t property_list;
#endif
if ((rpdata->application_data == NULL) ||
(rpdata->application_data_len == 0)) {
return 0;
}
apdu = rpdata->application_data;
if (property_list_common(rpdata->object_property)) {
apdu_len = property_list_common_encode(rpdata, Object_Instance_Number);
} else if (rpdata->object_property == PROP_OBJECT_NAME) {
/* only array properties can have array options */
if (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;
} else {
characterstring_init_ansi(&char_string, "");
if (pObject->Object_Name) {
(void)pObject->Object_Name(
rpdata->object_instance, &char_string);
}
apdu_len =
encode_application_character_string(&apdu[0], &char_string);
}
#if (BACNET_PROTOCOL_REVISION >= 14)
} else if (rpdata->object_property == PROP_PROPERTY_LIST) {
Device_Objects_Property_List(
rpdata->object_type, rpdata->object_instance, &property_list);
apdu_len = property_list_encode(rpdata, property_list.Required.pList,
property_list.Optional.pList, property_list.Proprietary.pList);
#endif
} else if (pObject->Object_Read_Property) {
apdu_len = pObject->Object_Read_Property(rpdata);
}
return apdu_len;
}
/** Looks up the requested Object and Property, and encodes its Value in an
* APDU.
* @ingroup ObjIntf
@@ -1092,10 +1229,14 @@ int Device_Read_Property(BACNET_READ_PROPERTY_DATA *rpdata)
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(rpdata->object_instance)) {
if (pObject->Object_Read_Property) {
apdu_len = pObject->Object_Read_Property(rpdata);
apdu_len = Read_Property_Common(pObject, rpdata);
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
}
} else {
rpdata->error_class = ERROR_CLASS_OBJECT;
rpdata->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
return apdu_len;
@@ -1109,7 +1250,10 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
BACNET_APPLICATION_DATA_VALUE value;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
int temp;
int result = 0;
#if defined(BACNET_TIME_MASTER)
uint32_t minutes = 0;
#endif
/* decode the some of the request */
len = bacapp_decode_application_data(
@@ -1173,12 +1317,13 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED);
if (status) {
temp = Device_Set_System_Status(
result = Device_Set_System_Status(
(BACNET_DEVICE_STATUS)value.type.Enumerated, false);
if (temp != 0) {
if (result != 0) {
/* result: - 0 = ok, -1 = bad value, -2 = not allowed */
status = false;
wp_data->error_class = ERROR_CLASS_PROPERTY;
if (temp == -1) {
if (result == -1) {
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
} else {
wp_data->error_code =
@@ -1236,9 +1381,67 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
characterstring_length(&value.type.Character_String));
}
break;
case PROP_MAX_INFO_FRAMES:
#if defined(BACNET_TIME_MASTER)
case PROP_TIME_SYNCHRONIZATION_INTERVAL:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int < 65535) {
minutes = value.type.Unsigned_Int;
Device_Time_Sync_Interval_Set(minutes);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_ALIGN_INTERVALS:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_BOOLEAN);
if (status) {
Device_Align_Intervals_Set(value.type.Boolean);
status = true;
}
break;
case PROP_INTERVAL_OFFSET:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int < 65535) {
minutes = value.type.Unsigned_Int;
Device_Interval_Offset_Set(minutes);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
#else
case PROP_TIME_SYNCHRONIZATION_INTERVAL:
case PROP_ALIGN_INTERVALS:
case PROP_INTERVAL_OFFSET:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
#endif
case PROP_UTC_OFFSET:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_SIGNED_INT);
if (status) {
if ((value.type.Signed_Int < (12 * 60)) &&
(value.type.Signed_Int > (-12 * 60))) {
Device_UTC_Offset_Set(value.type.Signed_Int);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
#if defined(BACDL_MSTP)
case PROP_MAX_INFO_FRAMES:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
@@ -1252,9 +1455,7 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
}
break;
#endif
case PROP_MAX_MASTER:
#if defined(BACDL_MSTP)
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
@@ -1268,13 +1469,18 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
}
break;
#else
case PROP_MAX_INFO_FRAMES:
case PROP_MAX_MASTER:
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
break;
#endif
case PROP_OBJECT_TYPE:
case PROP_VENDOR_NAME:
case PROP_FIRMWARE_REVISION:
case PROP_APPLICATION_SOFTWARE_VERSION:
case PROP_LOCAL_TIME:
case PROP_UTC_OFFSET:
case PROP_LOCAL_DATE:
case PROP_DAYLIGHT_SAVINGS_STATUS:
case PROP_PROTOCOL_VERSION:
@@ -1287,6 +1493,9 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
case PROP_DEVICE_ADDRESS_BINDING:
case PROP_DATABASE_REVISION:
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
#if defined(BACNET_TIME_MASTER)
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
#endif
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
break;
@@ -1321,7 +1530,15 @@ bool Device_Write_Property(BACNET_WRITE_PROPERTY_DATA *wp_data)
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(wp_data->object_instance)) {
if (pObject->Object_Write_Property) {
#if (BACNET_PROTOCOL_REVISION >= 14)
if (wp_data->object_property == PROP_PROPERTY_LIST) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else
#endif
{
status = pObject->Object_Write_Property(wp_data);
}
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
@@ -1338,6 +1555,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
@@ -1411,33 +1698,110 @@ void Device_COV_Clear(BACNET_OBJECT_TYPE object_type, uint32_t object_instance)
}
}
#if defined(INTRINSIC_REPORTING)
void Device_local_reporting(void)
/**
* @brief Creates a child object, if supported
* @ingroup ObjHelpers
* @param data - CreateObject data, including error codes if failures
* @return true if object has been created
*/
bool Device_Create_Object(
BACNET_CREATE_OBJECT_DATA *data)
{
struct object_functions *pObject;
uint32_t objects_count;
bool status = false;
struct object_functions *pObject = NULL;
uint32_t object_instance;
BACNET_OBJECT_TYPE object_type;
uint32_t idx;
objects_count = Device_Object_List_Count();
/* loop for all objects */
for (idx = 1; idx < objects_count; idx++) {
Device_Object_List_Identifier(idx, &object_type, &object_instance);
pObject = Device_Objects_Find_Functions(object_type);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_Intrinsic_Reporting) {
pObject->Object_Intrinsic_Reporting(object_instance);
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Create) {
/* The device supports the object type and may have
sufficient space, but does not support the creation of the
object for some other reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_DYNAMIC_CREATION_NOT_SUPPORTED;
} else if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being created already exists */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_IDENTIFIER_ALREADY_EXISTS;
} else {
if (data->list_of_initial_values) {
/* FIXME: add support for writing to list of initial values */
/* A property specified by the Property_Identifier in the
List of Initial Values does not support initialization
during the CreateObject service. */
data->first_failed_element_number = 1;
data->error_class = ERROR_CLASS_PROPERTY;
data->error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
/* and the object shall not be created */
} else {
object_instance = pObject->Object_Create(data->object_instance);
if (object_instance == BACNET_MAX_INSTANCE) {
/* The device cannot allocate the space needed
for the new object.*/
data->error_class = ERROR_CLASS_RESOURCES;
data->error_code = ERROR_CODE_NO_SPACE_FOR_OBJECT;
} else {
/* required by ACK */
data->object_instance = object_instance;
Device_Inc_Database_Revision();
status = true;
}
}
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
/**
* @brief Deletes a child object, if supported
* @ingroup ObjHelpers
* @param data - DeleteObject data, including error codes if failures
* @return true if object has been deleted
*/
bool Device_Delete_Object(
BACNET_DELETE_OBJECT_DATA *data)
{
bool status = false;
struct object_functions *pObject = NULL;
pObject = Device_Objects_Find_Functions(data->object_type);
if (pObject != NULL) {
if (!pObject->Object_Delete) {
/* The device supports the object type
but does not support the deletion of the
object for some reason.*/
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
} else if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(data->object_instance)) {
/* The object being deleted must already exist */
status = pObject->Object_Delete(data->object_instance);
if (status) {
Device_Inc_Database_Revision();
} else {
/* The object exists but cannot be deleted. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_OBJECT_DELETION_NOT_PERMITTED;
}
} else {
/* The object to be deleted does not exist. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNKNOWN_OBJECT;
}
} else {
/* The device does not support the specified object type. */
data->error_class = ERROR_CLASS_OBJECT;
data->error_code = ERROR_CODE_UNSUPPORTED_OBJECT_TYPE;
}
return status;
}
#endif
/** Looks up the requested Object to see if the functionality is supported.
* @ingroup ObjHelpers
@@ -1470,24 +1834,8 @@ bool Device_Value_List_Supported(BACNET_OBJECT_TYPE object_type)
void Device_Init(object_functions_t *object_table)
{
struct object_functions *pObject = NULL;
#if defined(BAC_UCI)
const char *uciname;
struct uci_context *ctx;
fprintf(stderr, "Device_Init\n");
ctx = ucix_init("bacnet_dev");
if (!ctx)
fprintf(stderr, "Failed to load config file bacnet_dev\n");
uciname = ucix_get_option(ctx, "bacnet_dev", "0", "Name");
if (uciname != 0) {
characterstring_init_ansi(&My_Object_Name, uciname);
} else {
#endif /* defined(BAC_UCI) */
characterstring_init_ansi(&My_Object_Name, "PiFace Digital Demo");
#if defined(BAC_UCI)
}
ucix_cleanup(ctx);
#endif /* defined(BAC_UCI) */
characterstring_init_ansi(&My_Object_Name, "PiFace");
datetime_init();
if (object_table) {
Object_Table = object_table;
} else {
@@ -1540,37 +1888,3 @@ bool DeviceGetRRInfo(BACNET_READ_RANGE_DATA *pRequest, /* Info on the request */
return status;
}
#ifdef BAC_ROUTING
/****************************************************************************
************* BACnet Routing Functionality (Optional) **********************
****************************************************************************
* The supporting functions are located in gw_device.c, except for those
* that need access to local data in this file.
****************************************************************************/
/** Initialize the first of our array of Devices with the main Device's
* information, and then swap out some of the Device object functions and
* replace with ones appropriate for routing.
* @ingroup ObjIntf
* @param first_object_instance Set the first (gateway) Device to this
instance number.
*/
void Routing_Device_Init(uint32_t first_object_instance)
{
struct object_functions *pDevObject = NULL;
/* Initialize with our preset strings */
Add_Routed_Device(first_object_instance, &My_Object_Name, Description);
/* Now substitute our routed versions of the main object functions. */
pDevObject = Object_Table;
pDevObject->Object_Index_To_Instance = Routed_Device_Index_To_Instance;
pDevObject->Object_Valid_Instance =
Routed_Device_Valid_Object_Instance_Number;
pDevObject->Object_Name = Routed_Device_Name;
pDevObject->Object_Read_Property = Routed_Device_Read_Property_Local;
pDevObject->Object_Write_Property = Routed_Device_Write_Property_Local;
}
#endif /* BAC_ROUTING */