feat: add Last_Restart_Reason property support to Device object (#1323)

This commit is contained in:
Steve Karg
2026-04-29 13:36:23 -05:00
committed by GitHub
parent fe74113318
commit 755233298b
5 changed files with 140 additions and 5 deletions
+2 -1
View File
@@ -1184,7 +1184,8 @@ typedef enum BACnetRestartReason {
compilers will allocate adequate sized datatype for enum
which is used to store decoding */
RESTART_REASON_PROPRIETARY_MIN = 64,
RESTART_REASON_PROPRIETARY_MAX = 255
RESTART_REASON_PROPRIETARY_MAX = 255,
BACNET_RESTART_REASON_MAX = 256
} BACNET_RESTART_REASON;
typedef enum BACnetPropertyStates {
+53 -3
View File
@@ -1279,6 +1279,7 @@ static const int32_t Device_Properties_Optional[] = {
PROP_ACTIVE_COV_SUBSCRIPTIONS,
PROP_SERIAL_NUMBER,
PROP_TIME_OF_DEVICE_RESTART,
PROP_LAST_RESTART_REASON,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_TIME_SYNCHRONIZATION_INTERVAL,
@@ -1331,7 +1332,10 @@ static const int32_t Writable_Properties[] = {
PROP_BACKUP_PREPARATION_TIME,
PROP_RESTORE_PREPARATION_TIME,
#endif
#if defined(BACAPP_TIMESTAMP)
PROP_TIME_OF_DEVICE_RESTART,
#endif
PROP_LAST_RESTART_REASON,
-1
};
@@ -1434,6 +1438,7 @@ static uint8_t Device_UUID[16];
/* static uint8_t Max_Segments_Accepted = 0; */
/* VT_Classes_Supported */
/* Active_VT_Sessions */
static BACNET_RESTART_REASON Last_Restart_Reason = RESTART_REASON_UNKNOWN;
static BACNET_TIMESTAMP Time_Of_Device_Restart;
static BACNET_TIME Local_Time; /* rely on OS, if there is one */
static BACNET_DATE Local_Date; /* rely on OS, if there is one */
@@ -2058,6 +2063,33 @@ bool Device_Serial_Number_Set(const char *str, size_t length)
return status;
}
/**
* @brief Set the device last-restart-reason property value.
* @param restart_reason [in] The new device last-restart-reason, as a
* BACNET_RESTART_REASON.
* @return true if the device last-restart-reason was set
*/
bool Device_Last_Restart_Reason_Set(const BACNET_RESTART_REASON restart_reason)
{
bool status = false; /*return value */
if (restart_reason < BACNET_RESTART_REASON_MAX) {
Last_Restart_Reason = restart_reason;
status = true;
}
return status;
}
/**
* @brief Get the device last-restart-reason property value.
* @return The device last-restart-reason, as a BACNET_RESTART_REASON.
*/
BACNET_RESTART_REASON Device_Last_Restart_Reason(void)
{
return Last_Restart_Reason;
}
void Device_Time_Of_Restart(BACNET_TIMESTAMP *time_of_restart)
{
bacapp_timestamp_copy(time_of_restart, &Time_Of_Device_Restart);
@@ -2559,10 +2591,10 @@ static BACNET_ERROR_CODE Device_Configuration_File_Write(
(void)object_instance;
if (array_index == 0) {
/* This array is not required to be resizable
through BACnet write services */
through BACnet write services */
(void)array_size;
error_code = ERROR_CODE_WRITE_ACCESS_DENIED;
} else {
} 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) {
@@ -2579,6 +2611,8 @@ static BACNET_ERROR_CODE Device_Configuration_File_Write(
} else {
error_code = ERROR_CODE_INVALID_DATA_TYPE;
}
} else {
error_code = ERROR_CODE_INVALID_ARRAY_INDEX;
}
return error_code;
@@ -2979,6 +3013,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len =
bacapp_encode_timestamp(&apdu[0], &Time_Of_Device_Restart);
break;
case PROP_LAST_RESTART_REASON:
apdu_len =
encode_application_enumerated(&apdu[0], Last_Restart_Reason);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -3351,14 +3389,26 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
}
break;
#endif
#if defined(BACAPP_TIMESTAMP)
case PROP_TIME_OF_DEVICE_RESTART:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_TIMESTAMP);
if (status) {
#if defined(BACAPP_TIMESTAMP)
bacapp_timestamp_copy(
&Time_Of_Device_Restart, &value.type.Time_Stamp);
}
break;
#endif
case PROP_LAST_RESTART_REASON:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED);
if (status) {
status = Device_Last_Restart_Reason_Set(
(BACNET_RESTART_REASON)value.type.Unsigned_Int);
if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
default:
+5
View File
@@ -417,6 +417,11 @@ const char *Device_Serial_Number(void);
BACNET_STACK_EXPORT
bool Device_Serial_Number_Set(const char *name, size_t length);
BACNET_STACK_EXPORT
bool Device_Last_Restart_Reason_Set(const BACNET_RESTART_REASON restart_reason);
BACNET_STACK_EXPORT
BACNET_RESTART_REASON Device_Last_Restart_Reason(void);
BACNET_STACK_EXPORT
void Device_Time_Of_Restart(BACNET_TIMESTAMP *time_of_restart);
BACNET_STACK_EXPORT
+46
View File
@@ -1215,6 +1215,7 @@ static const int32_t Device_Properties_Optional[] = {
#endif
PROP_SERIAL_NUMBER,
PROP_TIME_OF_DEVICE_RESTART,
PROP_LAST_RESTART_REASON,
#if defined(BACNET_TIME_MASTER)
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
PROP_TIME_SYNCHRONIZATION_INTERVAL,
@@ -1268,6 +1269,7 @@ static const int32_t Writable_Properties[] = {
PROP_RESTORE_PREPARATION_TIME,
#endif
PROP_TIME_OF_DEVICE_RESTART,
PROP_LAST_RESTART_REASON,
-1
};
@@ -1368,6 +1370,7 @@ static list_element_function Device_Add_List_Element_Callback;
static list_element_function Device_Remove_List_Element_Callback;
static uint8_t Device_UUID[16];
static const char *Serial_Number = BACNET_DEVICE_SERIAL_NUMBER;
static BACNET_RESTART_REASON Last_Restart_Reason = RESTART_REASON_UNKNOWN;
static BACNET_TIMESTAMP Time_Of_Device_Restart;
static BACNET_TIME Local_Time; /* rely on OS, if there is one */
static BACNET_DATE Local_Date; /* rely on OS, if there is one */
@@ -1933,6 +1936,33 @@ bool Device_Serial_Number_Set(const char *str, size_t length)
return true;
}
/**
* @brief Set the device last-restart-reason property value.
* @param restart_reason [in] The new device last-restart-reason, as a
* BACNET_RESTART_REASON.
* @return true if the device last-restart-reason was set
*/
bool Device_Last_Restart_Reason_Set(const BACNET_RESTART_REASON restart_reason)
{
bool status = false; /*return value */
if (restart_reason < BACNET_RESTART_REASON_MAX) {
Last_Restart_Reason = restart_reason;
status = true;
}
return status;
}
/**
* @brief Get the device last-restart-reason property value.
* @return The device last-restart-reason, as a BACNET_RESTART_REASON.
*/
BACNET_RESTART_REASON Device_Last_Restart_Reason(void)
{
return Last_Restart_Reason;
}
void Device_Time_Of_Restart(BACNET_TIMESTAMP *time_of_restart)
{
bacapp_timestamp_copy(time_of_restart, &Time_Of_Device_Restart);
@@ -2843,6 +2873,10 @@ int Device_Read_Property_Local(BACNET_READ_PROPERTY_DATA *rpdata)
apdu_len =
bacapp_encode_timestamp(&apdu[0], &Time_Of_Device_Restart);
break;
case PROP_LAST_RESTART_REASON:
apdu_len =
encode_application_enumerated(&apdu[0], Last_Restart_Reason);
break;
default:
rpdata->error_class = ERROR_CLASS_PROPERTY;
rpdata->error_code = ERROR_CODE_UNKNOWN_PROPERTY;
@@ -3225,6 +3259,18 @@ bool Device_Write_Property_Local(BACNET_WRITE_PROPERTY_DATA *wp_data)
#endif
}
break;
case PROP_LAST_RESTART_REASON:
status = write_property_type_valid(
wp_data, &value, BACNET_APPLICATION_TAG_ENUMERATED);
if (status) {
status = Device_Last_Restart_Reason_Set(
(BACNET_RESTART_REASON)value.type.Unsigned_Int);
if (!status) {
wp_data->error_class = ERROR_CLASS_PROPERTY;
wp_data->error_code = ERROR_CODE_VALUE_OUT_OF_RANGE;
}
}
break;
default:
if (property_lists_member(
Device_Properties_Required, Device_Properties_Optional,
@@ -464,6 +464,38 @@ static void testDevice(void)
zassert_true(property_list.Required.count > 0, NULL);
}
}
/**
* @brief Test Device_Last_Restart_Reason_Set() and Device_Last_Restart_Reason()
*/
static void test_Device_Last_Restart_Reason(void)
{
BACNET_RESTART_REASON reason;
bool status;
unsigned i;
Device_Init(NULL);
/* default value after init shall be RESTART_REASON_UNKNOWN */
reason = Device_Last_Restart_Reason();
zassert_equal(reason, RESTART_REASON_UNKNOWN, NULL);
/* loop over every valid value (0..BACNET_RESTART_REASON_MAX-1) */
for (i = 0; i < BACNET_RESTART_REASON_MAX; i++) {
reason = (BACNET_RESTART_REASON)i;
status = Device_Last_Restart_Reason_Set(reason);
zassert_true(status, "reason=%u rejected", i);
zassert_equal(
Device_Last_Restart_Reason(), reason, "reason=%u mismatch", i);
}
/* BACNET_RESTART_REASON_MAX (256) is out-of-range and must be rejected;
* the previously stored value shall be unchanged */
status = Device_Last_Restart_Reason_Set(BACNET_RESTART_REASON_MAX);
zassert_false(status, NULL);
zassert_equal(
Device_Last_Restart_Reason(),
(BACNET_RESTART_REASON)(BACNET_RESTART_REASON_MAX - 1), NULL);
}
/**
* @}
*/
@@ -475,7 +507,8 @@ void test_main(void)
{
ztest_test_suite(
device_tests, ztest_unit_test(testDevice),
ztest_unit_test(test_Device_Data_Sharing));
ztest_unit_test(test_Device_Data_Sharing),
ztest_unit_test(test_Device_Last_Restart_Reason));
ztest_run_test_suite(device_tests);
}