Feature/create object initial value backup (#1223)

* Added example Device object and property value backup using create-object list-of-initial-values and writable properties. Initiated using ReinitializeDevice with STARTBACKUP command. Added backup and restore required properties to the Device object.

* Fixed fwrite parameters in bacfile_posix_write_stream_data to return the correct number of bytes written.

* Added Device Management-Backup and Restore-B (DM-BR-B) conversion to Wireshark PCAP format command line utility in apps/dmbrcap which decodes and provides a view of the binary backup file.
This commit is contained in:
Steve Karg
2026-02-06 16:54:10 -06:00
committed by GitHub
parent 94e79efead
commit 1d1cf9bfac
17 changed files with 1897 additions and 72 deletions
+561 -27
View File
@@ -1091,14 +1091,21 @@ static const int32_t Device_Properties_Optional[] = {
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
PROP_ACTIVE_COV_SUBSCRIPTIONS,
#endif
PROP_SERIAL_NUMBER,
PROP_TIME_OF_DEVICE_RESTART,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_TIME_SYNCHRONIZATION_INTERVAL,
PROP_ALIGN_INTERVALS,
PROP_INTERVAL_OFFSET,
#endif
PROP_SERIAL_NUMBER,
PROP_TIME_OF_DEVICE_RESTART,
#if defined(BACNET_BACKUP_RESTORE)
PROP_CONFIGURATION_FILES,
PROP_BACKUP_FAILURE_TIMEOUT,
PROP_BACKUP_PREPARATION_TIME,
PROP_RESTORE_PREPARATION_TIME,
PROP_BACKUP_AND_RESTORE_STATE,
#endif
-1
};
@@ -1114,23 +1121,29 @@ static const int32_t Device_Properties_Proprietary[] = {
static const int32_t Writable_Properties[] = {
/* unordered list of writable properties */
PROP_OBJECT_IDENTIFIER,
PROP_NUMBER_OF_APDU_RETRIES,
PROP_APDU_TIMEOUT,
PROP_VENDOR_IDENTIFIER,
PROP_SYSTEM_STATUS,
PROP_OBJECT_NAME,
PROP_SYSTEM_STATUS,
PROP_VENDOR_IDENTIFIER,
PROP_MODEL_NAME,
PROP_LOCATION,
PROP_DESCRIPTION,
PROP_MODEL_NAME,
PROP_APDU_TIMEOUT,
PROP_NUMBER_OF_APDU_RETRIES,
PROP_UTC_OFFSET,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_INTERVAL,
PROP_ALIGN_INTERVALS,
PROP_INTERVAL_OFFSET,
#endif
PROP_UTC_OFFSET,
#if defined(BACDL_MSTP)
PROP_MAX_INFO_FRAMES,
PROP_MAX_MASTER,
#endif
#if defined(BACNET_BACKUP_RESTORE)
PROP_CONFIGURATION_FILES,
PROP_BACKUP_FAILURE_TIMEOUT,
PROP_BACKUP_PREPARATION_TIME,
PROP_RESTORE_PREPARATION_TIME,
#endif
PROP_TIME_OF_DEVICE_RESTART,
-1
@@ -1249,6 +1262,59 @@ static uint32_t Interval_Offset_Minutes;
/* Time_Synchronization_Recipients */
#endif
/* backup and restore */
#if defined BACNET_BACKUP_RESTORE
/* number of backup files */
#ifndef BACNET_BACKUP_FILE_COUNT
#if defined(BACFILE)
#define BACNET_BACKUP_FILE_COUNT 1
#else
#define BACNET_BACKUP_FILE_COUNT 0
#endif
#endif
#if BACNET_BACKUP_FILE_COUNT
/* device A will read the Configuration_Files property of the Device object.
This property will be used to determine the files to read and in what
order the files will be read. The value of the Configuration_Files
property is not guaranteed to contain a complete or correct set of
configuration File object references before the backup request is
accepted by device B. */
static uint32_t Configuration_Files[BACNET_BACKUP_FILE_COUNT];
#endif
/* If the restore is successful, no other actions by device A shall
be required, and device B will update the Last_Restore_Time property
in its Device object. */
static BACNET_TIMESTAMP Last_Restore_Time;
/* If device B does not receive any messages related to the restore
procedure from device A for the number of seconds specified
in the Backup_Failure_Timeout property of its Device object,
device B should assume that the restore procedure has been aborted,
and device B should exit restore mode.*/
static uint16_t Backup_Failure_Timeout;
/* This property indicates the amount of time in seconds
that the device might remain unresponsive after the sending
of a ReinitializeDevice-ACK at the start of a backup procedure.
The device that initiated the backup shall either wait the
period of time specified by this property or be prepared
to encounter communication timeouts during this period. */
static uint16_t Backup_Preparation_Time;
/* This property indicates the amount of time in seconds that the device
is allowed to remain unresponsive after the sending of a
ReinitializeDevice-ACK at the start of a restore procedure.
The restoring device shall either wait or be prepared to
encounter communication timeouts during this period.*/
static uint16_t Restore_Preparation_Time;
/* This property indicates the amount of time in seconds that
the device is allowed to remain unresponsive after the sending
of a ReinitializeDevice-ACK at the end of a restore procedure.
The restoring device shall either wait or be prepared to
encounter communication timeouts during this period. */
static uint16_t Restore_Completion_Time;
/* This property indicates a server device's backup and restore state. */
static BACNET_BACKUP_STATE Backup_State = BACKUP_STATE_IDLE;
#endif
/**
* @brief Sets the ReinitializeDevice password
*
@@ -1286,6 +1352,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
bool status = false;
bool password_success = false;
unsigned i;
size_t length;
/* From 16.4.1.1.2 Password
This optional parameter shall be a CharacterString of up to
@@ -1294,7 +1361,12 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
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(&Reinit_Password) > 0) {
if (characterstring_length(&rd_data->password) > 20) {
if (characterstring_encoding(&rd_data->password) == CHARACTER_UTF8) {
length = characterstring_utf8_length(&rd_data->password);
} else {
length = characterstring_length(&rd_data->password);
}
if (length > 20) {
rd_data->error_class = ERROR_CLASS_SERVICES;
rd_data->error_code = ERROR_CODE_PARAMETER_OUT_OF_RANGE;
} else if (characterstring_same(&rd_data->password, &Reinit_Password)) {
@@ -1309,19 +1381,46 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
if (password_success) {
switch (rd_data->state) {
case BACNET_REINIT_COLDSTART:
case BACNET_REINIT_WARMSTART:
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
/* Note: you could use a mix of state
and password to multiple things */
/* note: you probably want to restart *after* the
simple ack has been sent from the return handler
so just set a flag from here */
simple ack has been sent from the return handler
so just set a flag from here */
Reinitialize_State = rd_data->state;
status = true;
break;
case BACNET_REINIT_WARMSTART:
dcc_set_status_duration(COMMUNICATION_ENABLE, 0);
for (i = 0; i < Network_Port_Count(); i++) {
Network_Port_Changes_Pending_Activate(
Network_Port_Index_To_Instance(i));
}
/* note: you probably want to restart *after* the
simple ack has been sent from the return handler
so just set a flag from here */
Reinitialize_State = rd_data->state;
status = true;
break;
#if defined BACNET_BACKUP_RESTORE
case BACNET_REINIT_STARTBACKUP:
case BACNET_REINIT_ENDBACKUP:
Device_Start_Backup();
Reinitialize_State = rd_data->state;
status = true;
break;
case BACNET_REINIT_STARTRESTORE:
Device_Start_Restore();
Reinitialize_State = rd_data->state;
status = true;
break;
case BACNET_REINIT_ENDBACKUP:
case BACNET_REINIT_ENDRESTORE:
case BACNET_REINIT_ABORTRESTORE:
Reinitialize_State = rd_data->state;
status = true;
break;
#else
case BACNET_REINIT_STARTBACKUP:
case BACNET_REINIT_STARTRESTORE:
case BACNET_REINIT_ENDBACKUP:
case BACNET_REINIT_ENDRESTORE:
case BACNET_REINIT_ABORTRESTORE:
if (dcc_communication_disabled()) {
@@ -1333,6 +1432,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data)
ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED;
}
break;
#endif
case BACNET_REINIT_ACTIVATE_CHANGES:
/* note: activate changes *after* the simple ack is sent */
for (i = 0; i < Network_Port_Count(); i++) {
@@ -1390,7 +1490,9 @@ bool Device_Set_Object_Instance_Number(uint32_t object_id)
bool status = true; /* return value */
if (object_id <= BACNET_MAX_INSTANCE) {
/* Make the change and update the database revision */
Object_Instance_Number = object_id;
Device_Inc_Database_Revision();
} else {
status = false;
}
@@ -1519,16 +1621,58 @@ BACNET_DEVICE_STATUS Device_System_Status(void)
int Device_Set_System_Status(BACNET_DEVICE_STATUS status, bool local)
{
/*return value - 0 = ok, -1 = bad value, -2 = not allowed */
int result = -1;
int result = 0; /*return value - 0 = ok, -1 = bad value, -2 = not allowed */
(void)local;
if (status < MAX_DEVICE_STATUS) {
System_Status = status;
result = 0;
/* We limit the options available depending on whether the source is
* internal or external. */
if (local) {
switch (status) {
case STATUS_OPERATIONAL:
case STATUS_OPERATIONAL_READ_ONLY:
case STATUS_DOWNLOAD_REQUIRED:
case STATUS_DOWNLOAD_IN_PROGRESS:
case STATUS_NON_OPERATIONAL:
#if defined BACNET_BACKUP_RESTORE
case STATUS_BACKUP_IN_PROGRESS:
#endif
System_Status = status;
break;
#ifndef BACNET_BACKUP_RESTORE
case STATUS_BACKUP_IN_PROGRESS:
result = -2;
break;
#endif
default:
result = -1;
break;
}
} else {
switch (status) {
case STATUS_OPERATIONAL:
case STATUS_OPERATIONAL_READ_ONLY:
case STATUS_NON_OPERATIONAL:
/* Allow these for the moment as a way to easily alter
* overall device operation. The lack of password protection
* or other authentication makes allowing writes to this
* property a risky facility to provide. */
System_Status = status;
break;
case STATUS_DOWNLOAD_REQUIRED:
case STATUS_DOWNLOAD_IN_PROGRESS:
case STATUS_BACKUP_IN_PROGRESS:
/* Don't allow outsider set these - they should probably
* be set if the device config is incomplete or
* corrupted or perhaps after some sort of operator
* wipe operation. */
result = -2;
break;
default:
result = -1;
break;
}
}
return result;
return (result);
}
const char *Device_Vendor_Name(void)
@@ -1544,6 +1688,11 @@ bool Device_Set_Vendor_Name(const char *name, size_t length)
return true;
}
/** Returns the Vendor ID for this Device.
* See the assignments at
* https://bacnet.org/assigned-vendor-ids/
* @return The Vendor ID of this Device.
*/
uint16_t Device_Vendor_Identifier(void)
{
return Vendor_Identifier;
@@ -1670,7 +1819,11 @@ uint8_t Device_Protocol_Revision(void)
BACNET_SEGMENTATION Device_Segmentation_Supported(void)
{
#if BACNET_SEGMENTATION_ENABLED
return SEGMENTATION_BOTH;
#else
return SEGMENTATION_NONE;
#endif
}
/**
@@ -1824,7 +1977,7 @@ bool Device_Valid_Object_Name(
for (i = 1; i <= max_objects; i++) {
check_id = Device_Object_List_Identifier(i, &type, &instance);
if (check_id) {
pObject = Device_Object_Functions_Find((BACNET_OBJECT_TYPE)type);
pObject = Device_Object_Functions_Find(type);
if ((pObject != NULL) && (pObject->Object_Name != NULL) &&
(pObject->Object_Name(instance, &object_name2) &&
characterstring_same(object_name1, &object_name2))) {
@@ -1854,7 +2007,7 @@ bool Device_Valid_Object_Id(
bool status = false; /* return value */
struct object_functions *pObject = NULL;
pObject = Device_Object_Functions_Find((BACNET_OBJECT_TYPE)object_type);
pObject = Device_Object_Functions_Find(object_type);
if ((pObject != NULL) && (pObject->Object_Valid_Instance != NULL)) {
status = pObject->Object_Valid_Instance(object_instance);
}
@@ -1877,8 +2030,13 @@ bool Device_Object_Name_Copy(
bool found = false;
pObject = Device_Object_Functions_Find(object_type);
if ((pObject != NULL) && (pObject->Object_Name != NULL)) {
found = pObject->Object_Name(object_instance, object_name);
if (pObject != NULL) {
if (pObject->Object_Valid_Instance &&
pObject->Object_Valid_Instance(object_instance)) {
if (pObject->Object_Name) {
found = pObject->Object_Name(object_instance, object_name);
}
}
}
return found;
@@ -1989,6 +2147,187 @@ uint32_t Device_Interval_Offset(void)
}
#endif
bool Device_Configuration_File_Set(unsigned index, uint32_t instance)
{
bool status = false;
#if BACNET_BACKUP_FILE_COUNT
if (index < BACNET_BACKUP_FILE_COUNT) {
Configuration_Files[index] = instance;
status = true;
}
#else
(void)index;
(void)instance;
#endif
return status;
}
uint32_t Device_Configuration_File(unsigned index)
{
uint32_t instance = BACNET_MAX_INSTANCE + 1;
#if BACNET_BACKUP_FILE_COUNT
if (index < BACNET_BACKUP_FILE_COUNT) {
instance = Configuration_Files[index];
}
#else
(void)index;
#endif
return instance;
}
/**
* @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 Device_Configuration_File_Encode(
uint32_t object_instance, BACNET_ARRAY_INDEX array_index, uint8_t *apdu)
{
int apdu_len = BACNET_STATUS_ERROR;
(void)object_instance;
#if BACNET_BACKUP_FILE_COUNT
if (array_index < BACNET_BACKUP_FILE_COUNT) {
apdu_len = encode_application_object_id(
apdu, OBJECT_FILE, Configuration_Files[array_index]);
}
#else
(void)array_index;
(void)apdu;
#endif
return apdu_len;
}
#if defined BACNET_BACKUP_RESTORE
/**
* @brief Decode a BACnetLIST property element to determine the element length
* @param object_instance [in] BACnet object instance number
* @param apdu [in] Buffer in which the APDU contents are extracted
* @param apdu_size [in] The size of the APDU buffer
* @return The length of the decoded apdu, or BACNET_STATUS_ERROR on error
*/
static int Device_Configuration_File_Length(
uint32_t object_instance, uint8_t *apdu, size_t apdu_size)
{
(void)object_instance;
return bacnet_object_id_application_decode(apdu, apdu_size, NULL, NULL);
}
/**
* @brief Write a value to a BACnetLIST property element value
* using a BACnetARRAY write utility function
* @param object_instance [in] BACnet object instance number
* @param array_index [in] array index to write:
* 0=array size, 1 to N for individual array members
* @param application_data [in] encoded element value
* @param application_data_len [in] The size of the encoded element value
* @return BACNET_ERROR_CODE value
*/
static BACNET_ERROR_CODE Device_Configuration_File_Write(
uint32_t object_instance,
BACNET_ARRAY_INDEX array_index,
uint8_t *application_data,
size_t application_data_len)
{
BACNET_ERROR_CODE error_code = ERROR_CODE_UNKNOWN_OBJECT;
uint32_t instance = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
int len = 0;
(void)object_instance;
if (array_index == 0) {
/* This array is not required to be resizable
through BACnet write services */
error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else if (array_index <= BACNET_BACKUP_FILE_COUNT) {
len = bacnet_object_id_application_decode(
application_data, application_data_len, &object_type, &instance);
if (len > 0) {
if ((object_type == OBJECT_FILE) &&
Device_Valid_Object_Id(object_type, instance)) {
if (Device_Configuration_File_Set(array_index - 1, instance)) {
error_code = ERROR_CODE_SUCCESS;
} else {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
} else {
error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
} else {
error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
return error_code;
}
uint16_t Device_Backup_Failure_Timeout(void)
{
return Backup_Failure_Timeout;
}
bool Device_Backup_Failure_Timeout_Set(uint16_t timeout)
{
Backup_Failure_Timeout = timeout;
return true;
}
uint16_t Device_Backup_Preparation_Time(void)
{
return Backup_Preparation_Time;
}
bool Device_Backup_Preparation_Time_Set(uint16_t time)
{
Backup_Preparation_Time = time;
return true;
}
uint16_t Device_Restore_Preparation_Time(void)
{
return Restore_Preparation_Time;
}
bool Device_Restore_Preparation_Time_Set(uint16_t time)
{
Restore_Preparation_Time = time;
return true;
}
uint16_t Device_Restore_Completion_Time(void)
{
return Restore_Completion_Time;
}
bool Device_Restore_Completion_Time_Set(uint16_t time)
{
Restore_Completion_Time = time;
return true;
}
BACNET_BACKUP_STATE Device_Backup_And_Restore_State(void)
{
return Backup_State;
}
bool Device_Backup_And_Restore_State_Set(BACNET_BACKUP_STATE state)
{
Backup_State = state;
return true;
}
#endif
/**
* ReadProperty handler for this object. For the given ReadProperty
* data, the application_data is loaded or the error flags are set.
@@ -2128,8 +2467,12 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
rpdata->object_instance, rpdata->array_index,
Device_Object_List_Element_Encode, count, apdu, apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
#if BACNET_SEGMENTATION_ENABLED
rpdata->error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW;
#else
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
#endif
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
@@ -2187,6 +2530,45 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
encode_application_unsigned(&apdu[0], Device_Interval_Offset());
break;
#endif
#if defined(BACNET_BACKUP_RESTORE)
case PROP_CONFIGURATION_FILES:
apdu_len = bacnet_array_encode(
rpdata->object_instance, rpdata->array_index,
Device_Configuration_File_Encode, BACNET_BACKUP_FILE_COUNT,
&apdu[0], apdu_max);
if (apdu_len == BACNET_STATUS_ABORT) {
#if BACNET_SEGMENTATION_ENABLED
rpdata->error_code = ERROR_CODE_ABORT_BUFFER_OVERFLOW;
#else
rpdata->error_code =
ERROR_CODE_ABORT_SEGMENTATION_NOT_SUPPORTED;
#endif
} else if (apdu_len == BACNET_STATUS_ERROR) {
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
break;
case PROP_BACKUP_FAILURE_TIMEOUT:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Backup_Failure_Timeout());
break;
case PROP_BACKUP_PREPARATION_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Backup_Preparation_Time());
break;
case PROP_RESTORE_PREPARATION_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Restore_Preparation_Time());
break;
case PROP_RESTORE_COMPLETION_TIME:
apdu_len = encode_application_unsigned(
&apdu[0], Device_Restore_Completion_Time());
break;
case PROP_BACKUP_AND_RESTORE_STATE:
apdu_len = encode_application_enumerated(
&apdu[0], Device_Backup_And_Restore_State());
break;
#endif
#if (BACNET_COV_SUBSCRIPTIONS_SIZE > 0)
case PROP_ACTIVE_COV_SUBSCRIPTIONS:
if ((apdu_len = handler_cov_encode_subscriptions(
@@ -2469,6 +2851,60 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
}
break;
#endif
#if defined(BACNET_BACKUP_RESTORE)
case PROP_BACKUP_FAILURE_TIMEOUT:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int <= UINT16_MAX) {
Device_Backup_Failure_Timeout_Set(
(uint16_t)value.type.Unsigned_Int);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_BACKUP_PREPARATION_TIME:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int <= UINT16_MAX) {
Device_Backup_Preparation_Time_Set(
(uint16_t)value.type.Unsigned_Int);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_RESTORE_PREPARATION_TIME:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_UNSIGNED_INT);
if (status) {
if (value.type.Unsigned_Int <= UINT16_MAX) {
Device_Restore_Preparation_Time_Set(
(uint16_t)value.type.Unsigned_Int);
status = true;
} else {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
case PROP_CONFIGURATION_FILES:
wp_data->error_code = bacnet_array_write(
wp_data->object_instance, wp_data->array_index,
Device_Configuration_File_Length,
Device_Configuration_File_Write, BACNET_BACKUP_FILE_COUNT,
wp_data->application_data, wp_data->application_data_len);
if (wp_data->error_code == ERROR_CODE_SUCCESS) {
status = true;
}
break;
#endif
case PROP_UTC_OFFSET:
status = write_property_type_valid(
@@ -2759,7 +3195,9 @@ int Device_Remove_List_Element(BACNET_LIST_ELEMENT_DATA *list_element)
if (pObject->Object_Remove_List_Element) {
status = pObject->Object_Remove_List_Element(list_element);
if (status) {
Device_Remove_List_Element_Callback(list_element);
if (Device_Remove_List_Element_Callback) {
(void)Device_Remove_List_Element_Callback(list_element);
}
}
} else {
list_element->error_class = ERROR_CLASS_PROPERTY;
@@ -2932,6 +3370,102 @@ bool Device_Delete_Object(BACNET_DELETE_OBJECT_DATA *data)
return status;
}
/**
* @brief Loop through the Device object-list property and export to
* a file as BACnet CreateObject services with List of Initial Values
* for every writable property
*/
void Device_Start_Backup(void)
{
#if defined BACNET_BACKUP_RESTORE
size_t i = 0;
uint32_t object_count = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t object_instance = 0;
const int32_t *writable_properties = NULL;
struct special_property_list_t property_list = { 0 };
uint8_t object_apdu[MAX_APDU] = { 0 };
BACNET_CREATE_OBJECT_DATA create_data = { 0 };
bool status = false;
int32_t len = 0, offset = 0;
Backup_State = BACKUP_STATE_PREPARING_FOR_BACKUP;
object_count = Device_Object_List_Count();
Backup_State = BACKUP_STATE_PERFORMING_A_BACKUP;
for (i = 0; i < object_count; i++) {
/* get the object type and instance from the device object list */
status = Device_Object_List_Identifier(
(uint32_t)(i + 1), &object_type, &object_instance);
if (status) {
Device_Objects_Property_List(
object_type, object_instance, &property_list);
(void)Device_Objects_Writable_Property_List(
object_type, object_instance, &writable_properties);
create_data.object_type = object_type;
create_data.object_instance = object_instance;
create_data.application_data_len = 0;
len = create_object_writable_properties_encode(
object_apdu, sizeof(object_apdu), &create_data,
property_list.Required.pList, property_list.Optional.pList,
property_list.Proprietary.pList, writable_properties,
Device_Read_Property);
if (len > 0) {
#if defined(BACFILE)
(void)bacfile_write_offset(
Configuration_Files[0], offset, &object_apdu[0],
(uint32_t)len);
#endif
offset += len;
}
}
}
Backup_State = BACKUP_STATE_IDLE;
#endif
}
/**
* @brief Loop through the Device object-list property and export to
* a file as BACnet CreateObject services with List of Initial Values
* for every writable property
*/
void Device_Start_Restore(void)
{
#if defined BACNET_BACKUP_RESTORE
BACNET_DATE_TIME bdateTime = { 0 };
datetime_local(&bdateTime.date, &bdateTime.time, NULL, NULL);
bacapp_timestamp_datetime_set(&Last_Restore_Time, &bdateTime);
Backup_State = BACKUP_STATE_PREPARING_FOR_RESTORE;
#endif
}
#if defined(INTRINSIC_REPORTING)
void Device_local_reporting(void)
{
struct object_functions *pObject = NULL;
uint32_t objects_count = 0;
uint32_t object_instance = 0;
BACNET_OBJECT_TYPE object_type = OBJECT_NONE;
uint32_t idx = 0;
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_Object_Functions_Find(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);
}
}
}
}
}
#endif
/** Looks up the requested Object to see if the functionality is supported.
* @ingroup ObjHelpers
* @param [in] The object type to be looked up.