diff --git a/src/bacnet/basic/object/device.c b/src/bacnet/basic/object/device.c index bb25fd0c..a2825a95 100644 --- a/src/bacnet/basic/object/device.c +++ b/src/bacnet/basic/object/device.c @@ -1465,17 +1465,14 @@ static uint32_t Database_Revision = 0; /* Auto_Slave_Discovery */ /* Slave_Address_Binding */ /* Profile_Name */ -static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; -static const char *Reinit_Password = "filister"; +static BACNET_DEVICE_REINITIALIZE_DATA Reinitialize_Data = { + .State = BACNET_REINIT_IDLE, .Password = "filister" +}; static write_property_function Device_Write_Property_Store_Callback; static list_element_function Device_Add_List_Element_Callback; static list_element_function Device_Remove_List_Element_Callback; /* backup and restore */ #if defined BACNET_BACKUP_RESTORE -/* number of backup files */ -#ifndef BACNET_BACKUP_FILE_COUNT -#define BACNET_BACKUP_FILE_COUNT 1 -#endif /* 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 @@ -1520,6 +1517,140 @@ static BACNET_BACKUP_STATE Backup_State = BACKUP_STATE_IDLE; #ifdef BAC_ROUTING static bool Device_Router_Mode = false; + +static DEVICE_OBJECT_DATA *Device_Routed_Data(void) +{ + if (Device_Router_Mode) { + return Get_Routed_Device_Object(-1); + } + + return NULL; +} + +static bool Device_Routed_Virtual_Device(void) +{ + return Device_Router_Mode && (Routed_Device_Object_Index() > 0); +} + +#endif + +static BACNET_DEVICE_REINITIALIZE_DATA *Device_Reinitialize_Data(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Reinitialize; + } +#endif + + return &Reinitialize_Data; +} + +#if defined(BACNET_BACKUP_RESTORE) +static uint32_t *Device_Configuration_Files_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return pDev->Backup.Configuration_Files; + } +#endif + + return Configuration_Files; +} + +static BACNET_TIMESTAMP *Device_Last_Restore_Time_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Last_Restore_Time; + } +#endif + + return &Last_Restore_Time; +} + +static uint16_t *Device_Backup_Failure_Timeout_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Backup_Failure_Timeout; + } +#endif + + return &Backup_Failure_Timeout; +} + +static uint32_t *Device_Backup_Failure_Timeout_Milliseconds_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Backup_Failure_Timeout_Milliseconds; + } +#endif + + return &Backup_Failure_Timeout_Milliseconds; +} + +static uint16_t *Device_Backup_Preparation_Time_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Backup_Preparation_Time; + } +#endif + + return &Backup_Preparation_Time; +} + +static uint16_t *Device_Restore_Preparation_Time_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Restore_Preparation_Time; + } +#endif + + return &Restore_Preparation_Time; +} + +static uint16_t *Device_Restore_Completion_Time_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Restore_Completion_Time; + } +#endif + + return &Restore_Completion_Time; +} + +static BACNET_BACKUP_STATE *Device_Backup_State_Value(void) +{ +#if defined(BAC_ROUTING) + DEVICE_OBJECT_DATA *pDev = Device_Routed_Data(); + + if (pDev) { + return &pDev->Backup.Backup_State; + } +#endif + + return &Backup_State; +} #endif /** @@ -1535,7 +1666,10 @@ static bool Device_Router_Mode = false; */ bool Device_Reinitialize_Password_Set(const char *password) { - Reinit_Password = password; + BACNET_DEVICE_REINITIALIZE_DATA *reinitialize_data = + Device_Reinitialize_Data(); + + reinitialize_data->Password = password; return true; } @@ -1559,6 +1693,13 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) bool password_success = false; unsigned i; size_t length; + BACNET_DEVICE_REINITIALIZE_DATA *reinitialize_data = + Device_Reinitialize_Data(); + BACNET_REINITIALIZED_STATE *reinitialize_state = &reinitialize_data->State; + const char *reinit_password = reinitialize_data->Password; +#if defined BACNET_BACKUP_RESTORE + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); +#endif /* From 16.4.1.1.2 Password This optional parameter shall be a CharacterString of up to @@ -1566,7 +1707,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) 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 (Reinit_Password && strlen(Reinit_Password) > 0) { + if (reinit_password && strlen(reinit_password) > 0) { if (characterstring_encoding(&rd_data->password) == CHARACTER_UTF8) { length = characterstring_utf8_length(&rd_data->password); } else { @@ -1576,7 +1717,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) 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)) { + &rd_data->password, reinit_password)) { password_success = true; } else { rd_data->error_class = ERROR_CLASS_SECURITY; @@ -1586,13 +1727,24 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) password_success = true; } if (password_success) { +#if defined(BAC_ROUTING) + if (Device_Routed_Virtual_Device() && + ((rd_data->state == BACNET_REINIT_COLDSTART) || + (rd_data->state == BACNET_REINIT_WARMSTART) || + (rd_data->state == BACNET_REINIT_ACTIVATE_CHANGES))) { + rd_data->error_class = ERROR_CLASS_SERVICES; + rd_data->error_code = + ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED; + return false; + } +#endif switch (rd_data->state) { case BACNET_REINIT_COLDSTART: dcc_set_status_duration(COMMUNICATION_ENABLE, 0); /* 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; + *reinitialize_state = rd_data->state; status = true; break; case BACNET_REINIT_WARMSTART: @@ -1604,50 +1756,50 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) /* 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; + *reinitialize_state = rd_data->state; status = true; break; #if defined BACNET_BACKUP_RESTORE case BACNET_REINIT_STARTBACKUP: - if (Device_Backup_State_In_Progress(Backup_State)) { + if (Device_Backup_State_In_Progress(*backup_state)) { rd_data->error_class = ERROR_CLASS_DEVICE; rd_data->error_code = ERROR_CODE_CONFIGURATION_IN_PROGRESS; break; } - Backup_State = BACKUP_STATE_PREPARING_FOR_BACKUP; + *backup_state = BACKUP_STATE_PREPARING_FOR_BACKUP; Device_Backup_Failure_Timeout_Restart(); Device_Start_Backup(); - Reinitialize_State = rd_data->state; + *reinitialize_state = rd_data->state; status = true; break; case BACNET_REINIT_STARTRESTORE: - if (Device_Backup_State_In_Progress(Backup_State)) { + if (Device_Backup_State_In_Progress(*backup_state)) { rd_data->error_class = ERROR_CLASS_DEVICE; rd_data->error_code = ERROR_CODE_CONFIGURATION_IN_PROGRESS; break; } - Backup_State = BACKUP_STATE_PREPARING_FOR_RESTORE; + *backup_state = BACKUP_STATE_PREPARING_FOR_RESTORE; Device_Backup_Failure_Timeout_Restart(); Device_Start_Restore(); - Reinitialize_State = rd_data->state; + *reinitialize_state = rd_data->state; status = true; break; case BACNET_REINIT_ENDRESTORE: - if (Backup_State != BACKUP_STATE_PERFORMING_A_RESTORE) { + if (*backup_state != BACKUP_STATE_PERFORMING_A_RESTORE) { rd_data->error_class = ERROR_CLASS_DEVICE; rd_data->error_code = ERROR_CODE_CONFIGURATION_IN_PROGRESS; break; } Device_Backup_Failure_Timeout_Restart(); Device_End_Restore(); - Reinitialize_State = rd_data->state; + *reinitialize_state = rd_data->state; status = true; break; case BACNET_REINIT_ENDBACKUP: case BACNET_REINIT_ABORTRESTORE: - Backup_State = BACKUP_STATE_IDLE; + *backup_state = BACKUP_STATE_IDLE; Device_Backup_Failure_Timeout_Reset(); - Reinitialize_State = rd_data->state; + *reinitialize_state = rd_data->state; status = true; break; #else @@ -1672,7 +1824,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) Network_Port_Changes_Pending_Activate( Network_Port_Index_To_Instance(i)); } - Reinitialize_State = rd_data->state; + *reinitialize_state = rd_data->state; status = true; break; default: @@ -1687,12 +1839,13 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) BACNET_REINITIALIZED_STATE Device_Reinitialized_State(void) { - return Reinitialize_State; + return Device_Reinitialize_Data()->State; } bool Device_Reinitialize_State_Set(BACNET_REINITIALIZED_STATE state) { - Reinitialize_State = state; + Device_Reinitialize_Data()->State = state; + return true; } @@ -2467,9 +2620,11 @@ uint32_t Device_Interval_Offset(void) bool Device_Configuration_File_Set(unsigned index, uint32_t instance) { bool status = false; -#if BACNET_BACKUP_FILE_COUNT +#if defined(BACNET_BACKUP_RESTORE) + uint32_t *configuration_files = Device_Configuration_Files_Value(); + if (index < BACNET_BACKUP_FILE_COUNT) { - Configuration_Files[index] = instance; + configuration_files[index] = instance; status = true; } #else @@ -2483,10 +2638,11 @@ bool Device_Configuration_File_Set(unsigned index, uint32_t instance) uint32_t Device_Configuration_File(unsigned index) { uint32_t instance = BACNET_MAX_INSTANCE + 1; +#if defined(BACNET_BACKUP_RESTORE) + uint32_t *configuration_files = Device_Configuration_Files_Value(); -#if BACNET_BACKUP_FILE_COUNT if (index < BACNET_BACKUP_FILE_COUNT) { - instance = Configuration_Files[index]; + instance = configuration_files[index]; } #else (void)index; @@ -2505,11 +2661,12 @@ uint32_t Device_Configuration_File(unsigned index) bool Device_Is_Configuration_File(uint32_t instance) { bool status = false; - -#if BACNET_BACKUP_FILE_COUNT +#if defined(BACNET_BACKUP_RESTORE) unsigned i; + uint32_t *configuration_files = Device_Configuration_Files_Value(); + for (i = 0; i < BACNET_BACKUP_FILE_COUNT; i++) { - if (Configuration_Files[i] == instance) { + if (configuration_files[i] == instance) { status = true; break; } @@ -2537,10 +2694,12 @@ int Device_Configuration_File_Encode( int apdu_len = BACNET_STATUS_ERROR; (void)object_instance; -#if BACNET_BACKUP_FILE_COUNT +#if defined(BACNET_BACKUP_RESTORE) + uint32_t *configuration_files = Device_Configuration_Files_Value(); + if (array_index < BACNET_BACKUP_FILE_COUNT) { apdu_len = encode_application_object_id( - apdu, OBJECT_FILE, Configuration_Files[array_index]); + apdu, OBJECT_FILE, configuration_files[array_index]); } #else (void)array_index; @@ -2620,56 +2779,67 @@ static BACNET_ERROR_CODE Device_Configuration_File_Write( uint16_t Device_Backup_Failure_Timeout(void) { - return Backup_Failure_Timeout; + return *Device_Backup_Failure_Timeout_Value(); } bool Device_Backup_Failure_Timeout_Set(uint16_t timeout) { - Backup_Failure_Timeout = timeout; + uint16_t *backup_failure_timeout = Device_Backup_Failure_Timeout_Value(); + + *backup_failure_timeout = timeout; return true; } uint16_t Device_Backup_Preparation_Time(void) { - return Backup_Preparation_Time; + return *Device_Backup_Preparation_Time_Value(); } bool Device_Backup_Preparation_Time_Set(uint16_t time) { - Backup_Preparation_Time = time; + uint16_t *backup_preparation_time = Device_Backup_Preparation_Time_Value(); + + *backup_preparation_time = time; return true; } uint16_t Device_Restore_Preparation_Time(void) { - return Restore_Preparation_Time; + return *Device_Restore_Preparation_Time_Value(); } bool Device_Restore_Preparation_Time_Set(uint16_t time) { - Restore_Preparation_Time = time; + uint16_t *restore_preparation_time = + Device_Restore_Preparation_Time_Value(); + + *restore_preparation_time = time; return true; } uint16_t Device_Restore_Completion_Time(void) { - return Restore_Completion_Time; + return *Device_Restore_Completion_Time_Value(); } bool Device_Restore_Completion_Time_Set(uint16_t time) { - Restore_Completion_Time = time; + uint16_t *restore_completion_time = Device_Restore_Completion_Time_Value(); + + *restore_completion_time = time; return true; } BACNET_BACKUP_STATE Device_Backup_And_Restore_State(void) { - return Backup_State; + return *Device_Backup_State_Value(); } bool Device_Backup_And_Restore_State_Set(BACNET_BACKUP_STATE state) { - Backup_State = state; + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + + *backup_state = state; return true; } @@ -2680,7 +2850,10 @@ bool Device_Backup_And_Restore_State_Set(BACNET_BACKUP_STATE state) */ void Device_Backup_Failure_Timeout_Reset(void) { - Backup_Failure_Timeout_Milliseconds = 0; + uint32_t *backup_failure_timeout_milliseconds = + Device_Backup_Failure_Timeout_Milliseconds_Value(); + + *backup_failure_timeout_milliseconds = 0; } #endif @@ -2709,10 +2882,15 @@ bool Device_Backup_State_In_Progress(BACNET_BACKUP_STATE state) void Device_Backup_Failure_Timeout_Restart(void) { #if defined(BACNET_BACKUP_RESTORE) - if (Device_Backup_State_In_Progress(Backup_State)) { + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + uint16_t *backup_failure_timeout = Device_Backup_Failure_Timeout_Value(); + uint32_t *backup_failure_timeout_milliseconds = + Device_Backup_Failure_Timeout_Milliseconds_Value(); + + if (Device_Backup_State_In_Progress(*backup_state)) { /* service related to backup & restore will reset the backup failure timeout during a backup or restore operation */ - Backup_Failure_Timeout_Milliseconds = Backup_Failure_Timeout * 1000UL; + *backup_failure_timeout_milliseconds = *backup_failure_timeout * 1000UL; } #endif } @@ -2734,20 +2912,24 @@ void Device_Backup_Failure_Timeout_Restart(void) void Device_Backup_Failure_Timeout_Countdown(uint32_t milliseconds) { #if defined(BACNET_BACKUP_RESTORE) - if (Device_Backup_State_In_Progress(Backup_State)) { + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + uint32_t *backup_failure_timeout_milliseconds = + Device_Backup_Failure_Timeout_Milliseconds_Value(); + + if (Device_Backup_State_In_Progress(*backup_state)) { /* service related to backup & restore will restart the backup failure timer during a backup or restore operation */ - if (Backup_Failure_Timeout_Milliseconds > 0) { - if (milliseconds >= Backup_Failure_Timeout_Milliseconds) { - Backup_Failure_Timeout_Milliseconds = 0; + if (*backup_failure_timeout_milliseconds > 0) { + if (milliseconds >= *backup_failure_timeout_milliseconds) { + *backup_failure_timeout_milliseconds = 0; } else { - Backup_Failure_Timeout_Milliseconds -= milliseconds; + *backup_failure_timeout_milliseconds -= milliseconds; } - if (Backup_Failure_Timeout_Milliseconds == 0) { - if (Backup_State == BACKUP_STATE_PERFORMING_A_BACKUP) { - Backup_State = BACKUP_STATE_BACKUP_FAILURE; - } else if (Backup_State == BACKUP_STATE_PERFORMING_A_RESTORE) { - Backup_State = BACKUP_STATE_RESTORE_FAILURE; + if (*backup_failure_timeout_milliseconds == 0) { + if (*backup_state == BACKUP_STATE_PERFORMING_A_BACKUP) { + *backup_state = BACKUP_STATE_BACKUP_FAILURE; + } else if (*backup_state == BACKUP_STATE_PERFORMING_A_RESTORE) { + *backup_state = BACKUP_STATE_RESTORE_FAILURE; } } } @@ -3888,6 +4070,8 @@ void Device_Start_Backup(void) BACNET_CREATE_OBJECT_DATA create_data = { 0 }; bool status = false; int32_t len = 0, offset = 0; + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + uint32_t *configuration_files = Device_Configuration_Files_Value(); object_count = Device_Object_List_Count(); for (i = 0; i < object_count; i++) { @@ -3909,13 +4093,13 @@ void Device_Start_Backup(void) Device_Read_Property); if (len > 0) { (void)bacfile_write_offset( - Configuration_Files[0], offset, &object_apdu[0], + configuration_files[0], offset, &object_apdu[0], (uint32_t)len); offset += len; } } } - Backup_State = BACKUP_STATE_PERFORMING_A_BACKUP; + *backup_state = BACKUP_STATE_PERFORMING_A_BACKUP; #endif } @@ -3926,7 +4110,9 @@ void Device_Start_Backup(void) void Device_Start_Restore(void) { #if defined BACNET_BACKUP_RESTORE - Backup_State = BACKUP_STATE_PERFORMING_A_RESTORE; + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + + *backup_state = BACKUP_STATE_PERFORMING_A_RESTORE; #endif } @@ -3943,16 +4129,19 @@ void Device_End_Restore(void) uint8_t apdu[MAX_APDU] = { 0 }; int32_t apdu_len = 0, offset = 0, file_size = 0; int decoded_len = 0; + BACNET_BACKUP_STATE *backup_state = Device_Backup_State_Value(); + BACNET_TIMESTAMP *last_restore_time = Device_Last_Restore_Time_Value(); + uint32_t *configuration_files = Device_Configuration_Files_Value(); datetime_local(&bdateTime.date, &bdateTime.time, NULL, NULL); - bacapp_timestamp_datetime_set(&Last_Restore_Time, &bdateTime); + bacapp_timestamp_datetime_set(last_restore_time, &bdateTime); /* delete all existing objects before restore */ Device_Delete_Objects(); /* create objects from the backup file */ - file_size = bacfile_file_size(Configuration_Files[0]); + file_size = bacfile_file_size(configuration_files[0]); while (offset < file_size) { apdu_len = bacfile_read_offset( - Configuration_Files[0], offset, &apdu[0], sizeof(apdu)); + configuration_files[0], offset, &apdu[0], sizeof(apdu)); if (apdu_len > 0) { decoded_len = create_object_decode_service_request( apdu, apdu_len, &create_data); @@ -3967,18 +4156,18 @@ void Device_End_Restore(void) /* error creating object - keep going */ } } else { - Backup_State = BACKUP_STATE_RESTORE_FAILURE; + *backup_state = BACKUP_STATE_RESTORE_FAILURE; /* error while decoding object */ break; } } else { - Backup_State = BACKUP_STATE_RESTORE_FAILURE; + *backup_state = BACKUP_STATE_RESTORE_FAILURE; /* error while reading file */ break; } } - if (Backup_State != BACKUP_STATE_RESTORE_FAILURE) { - Backup_State = BACKUP_STATE_IDLE; + if (*backup_state != BACKUP_STATE_RESTORE_FAILURE) { + *backup_state = BACKUP_STATE_IDLE; } #endif } @@ -4163,23 +4352,31 @@ void Device_Timer(uint16_t milliseconds) #ifdef BAC_ROUTING uint16_t dev_id = 0; uint16_t current_dev_id = Routed_Device_Object_Index(); - /* TODO: Multi-device Backup/Restore support - * Currently only Gateway (dev_id=0) supports Backup/Restore. - * Virtual devices are blocked by Routed_Device_Service_Approval(). - * Plan to support after discussing with maintainer. - * - * To enable per-device Backup/Restore: - * 1. Move Backup-related static variables to DEVICE_OBJECT_DATA: - * - Backup_State, Backup_Failure_Timeout_Milliseconds - * - Backup_Failure_Timeout, Configuration_Files[] - * - Last_Restore_Time, Backup/Restore_Preparation_Time - * 2. Modify Routed_Device_Service_Approval() to allow RD on virtual devices - * 3. Call Device_Backup_Failure_Timeout_Countdown() inside the for loop - * for each device - */ - Device_Backup_Failure_Timeout_Countdown(milliseconds); - for (dev_id = 0; dev_id < Get_Num_Managed_Devices(); dev_id++) { - Set_Routed_Device_Object_Index(dev_id); + + if (Device_Router_Mode) { + for (dev_id = 0; dev_id < Get_Num_Managed_Devices(); dev_id++) { + Set_Routed_Device_Object_Index(dev_id); + Device_Backup_Failure_Timeout_Countdown(milliseconds); + pObject = Object_Table; + while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { + count = 0; + if (pObject->Object_Count) { + count = pObject->Object_Count(); + } + while (count) { + count--; + if ((pObject->Object_Timer) && + (pObject->Object_Index_To_Instance)) { + instance = pObject->Object_Index_To_Instance(count); + pObject->Object_Timer(instance, milliseconds); + } + } + pObject++; + } + } + Set_Routed_Device_Object_Index(current_dev_id); + } else { + Device_Backup_Failure_Timeout_Countdown(milliseconds); pObject = Object_Table; while (pObject->Object_Type < MAX_BACNET_OBJECT_TYPE) { count = 0; @@ -4197,7 +4394,6 @@ void Device_Timer(uint16_t milliseconds) pObject++; } } - Set_Routed_Device_Object_Index(current_dev_id); #else Device_Backup_Failure_Timeout_Countdown(milliseconds); pObject = Object_Table; @@ -4240,6 +4436,7 @@ void Routing_Device_Init(uint32_t first_object_instance) Device_Router_Mode = true; /* Initialize with our preset strings */ + Routed_Device_Table_Reset(); Add_Routed_Device(first_object_instance, &My_Object_Name, Description); /* Now substitute our routed versions of the main object functions. */ diff --git a/src/bacnet/basic/object/device.h b/src/bacnet/basic/object/device.h index 3ac84b90..18695ce2 100644 --- a/src/bacnet/basic/object/device.h +++ b/src/bacnet/basic/object/device.h @@ -178,6 +178,34 @@ typedef struct commonBacObj_s { * This may be useful for implementations which manage multiple Devices, * eg, a Gateway. */ +/* number of backup files */ +#if defined(BACNET_BACKUP_RESTORE) +#ifndef BACNET_BACKUP_FILE_COUNT +#define BACNET_BACKUP_FILE_COUNT 1 +#elif (BACNET_BACKUP_FILE_COUNT < 1) +#error \ + "BACNET_BACKUP_FILE_COUNT must be at least 1 when BACNET_BACKUP_RESTORE is enabled" +#endif +#endif + +#if defined(BAC_ROUTING) && defined(BACNET_BACKUP_RESTORE) +typedef struct bacnet_backup_restore_data_s { + BACNET_BACKUP_STATE Backup_State; + uint16_t Backup_Failure_Timeout; + uint32_t Backup_Failure_Timeout_Milliseconds; + uint32_t Configuration_Files[BACNET_BACKUP_FILE_COUNT]; + BACNET_TIMESTAMP Last_Restore_Time; + uint16_t Backup_Preparation_Time; + uint16_t Restore_Preparation_Time; + uint16_t Restore_Completion_Time; +} BACNET_BACKUP_RESTORE_DATA; +#endif + +typedef struct bacnet_device_reinitialize_data_s { + BACNET_REINITIALIZED_STATE State; + const char *Password; +} BACNET_DEVICE_REINITIALIZE_DATA; + typedef struct devObj_s { /** The BACnet Device Address for this device; ->len depends on DLL type. */ BACNET_ADDRESS bacDevAddr; @@ -191,6 +219,13 @@ typedef struct devObj_s { /** The upcounter that shows if the Device ID or object structure has * changed. */ uint32_t Database_Revision; + +#if defined(BAC_ROUTING) + BACNET_DEVICE_REINITIALIZE_DATA Reinitialize; +#if defined(BACNET_BACKUP_RESTORE) + BACNET_BACKUP_RESTORE_DATA Backup; +#endif +#endif } DEVICE_OBJECT_DATA; #ifdef __cplusplus @@ -502,6 +537,8 @@ uint16_t Add_Routed_Device( const BACNET_CHARACTER_STRING *Object_Name, const char *Description); BACNET_STACK_EXPORT +void Routed_Device_Table_Reset(void); +BACNET_STACK_EXPORT DEVICE_OBJECT_DATA *Get_Routed_Device_Object(int idx); BACNET_STACK_EXPORT BACNET_ADDRESS *Get_Routed_Device_Address(int idx); diff --git a/src/bacnet/basic/object/gateway/gw_device.c b/src/bacnet/basic/object/gateway/gw_device.c index fca270eb..81c67075 100644 --- a/src/bacnet/basic/object/gateway/gw_device.c +++ b/src/bacnet/basic/object/gateway/gw_device.c @@ -75,6 +75,14 @@ uint16_t Num_Managed_Devices = 0; */ uint16_t iCurrent_Device_Idx = 0; +/** Reset the routed Device table before rebuilding the gateway/virtual list. */ +void Routed_Device_Table_Reset(void) +{ + memset(&Devices[0], 0, sizeof(Devices)); + Num_Managed_Devices = 0; + iCurrent_Device_Idx = 0; +} + /** Get the current routed device object index. * @return Index of the currently active routed device in Devices[] array */ @@ -150,6 +158,15 @@ uint16_t Add_Routed_Device( Routed_Device_Set_Description("No Descr", strlen("No Descr")); } pDev->Database_Revision = 0; /* Reset/Initialize now */ +#if defined(BAC_ROUTING) + pDev->Reinitialize.State = BACNET_REINIT_IDLE; + pDev->Reinitialize.Password = "filister"; +#if defined(BACNET_BACKUP_RESTORE) + memset(&pDev->Backup, 0, sizeof(pDev->Backup)); + pDev->Backup.Backup_State = BACKUP_STATE_IDLE; + pDev->Backup.Backup_Failure_Timeout = 60 * 60; +#endif +#endif return i; } else { return UINT16_MAX; @@ -606,8 +623,8 @@ void Routed_Device_Inc_Database_Revision(void) } /** Check to see if the current Device supports this service. - * Presently checks for RD and DCC and only allows them if the current - * device is the gateway device. + * Presently allows ReinitializeDevice for routed virtual devices and keeps + * DeviceCommunicationControl restricted to the gateway device. * * @param service [in] The service being requested. * @param service_argument [in] An optional argument (eg, service type). @@ -629,16 +646,9 @@ int Routed_Device_Service_Approval( (void)service_argument; switch (service) { case SERVICE_SUPPORTED_REINITIALIZE_DEVICE: - /* If not the gateway device, we don't support RD */ - if (iCurrent_Device_Idx > 0) { - if (apdu_buff != NULL) { - len = reject_encode_apdu( - apdu_buff, invoke_id, - REJECT_REASON_UNRECOGNIZED_SERVICE); - } else { - len = 1; /* Non-zero return */ - } - } + /* RD is accepted for virtual devices. Device_Reinitialize() + handles state-specific support and returns service errors for + virtual-device states that have gateway/system side effects. */ break; case SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL: /* If not the gateway device, we don't support DCC */ diff --git a/src/bacnet/basic/server/bacnet_device.c b/src/bacnet/basic/server/bacnet_device.c index f677d89a..5672cec0 100644 --- a/src/bacnet/basic/server/bacnet_device.c +++ b/src/bacnet/basic/server/bacnet_device.c @@ -1364,7 +1364,7 @@ static const char *Device_Location_Default = BACNET_DEVICE_LOCATION_NAME; static const char *Device_Description_Default = BACNET_DEVICE_DESCRIPTION; static uint32_t Database_Revision; static BACNET_REINITIALIZED_STATE Reinitialize_State = BACNET_REINIT_IDLE; -static BACNET_CHARACTER_STRING Reinit_Password; +static const char *Reinit_Password = "filister"; static write_property_function Device_Write_Property_Store_Callback; static list_element_function Device_Add_List_Element_Callback; static list_element_function Device_Remove_List_Element_Callback; @@ -1448,7 +1448,7 @@ static BACNET_BACKUP_STATE Backup_State = BACKUP_STATE_IDLE; */ bool Device_Reinitialize_Password_Set(const char *password) { - characterstring_init_ansi(&Reinit_Password, password); + Reinit_Password = password; return true; } @@ -1480,7 +1480,7 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) 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(&Reinit_Password) > 0) { + if (Reinit_Password && strlen(Reinit_Password) > 0) { if (characterstring_encoding(&rd_data->password) == CHARACTER_UTF8) { length = characterstring_utf8_length(&rd_data->password); } else { @@ -1489,7 +1489,8 @@ bool Device_Reinitialize(BACNET_REINITIALIZE_DEVICE_DATA *rd_data) 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)) { + } else if (characterstring_ansi_same( + &rd_data->password, Reinit_Password)) { password_success = true; } else { rd_data->error_class = ERROR_CLASS_SECURITY; diff --git a/test/bacnet/basic/object/device/CMakeLists.txt b/test/bacnet/basic/object/device/CMakeLists.txt index 3af5b363..80d66a32 100644 --- a/test/bacnet/basic/object/device/CMakeLists.txt +++ b/test/bacnet/basic/object/device/CMakeLists.txt @@ -30,7 +30,7 @@ include_directories( ${TST_DIR}/ztest/include ) -add_executable(${PROJECT_NAME} +set(TEST_DEVICE_SOURCES # File(s) under test ${SRC_DIR}/bacnet/basic/object/device.c # Support files and stubs (pathname alphabetical) @@ -140,3 +140,65 @@ add_executable(${PROJECT_NAME} ${ZTST_DIR}/ztest_mock.c ${ZTST_DIR}/ztest.c ) + +function(configure_device_test_target + target_name routing_enabled backup_restore_enabled) + if(routing_enabled) + target_sources(${target_name} PRIVATE + ${SRC_DIR}/bacnet/basic/object/gateway/gw_device.c + ) + target_compile_definitions(${target_name} PRIVATE + BAC_ROUTING + ) + endif() + if(backup_restore_enabled) + target_compile_definitions(${target_name} PRIVATE + BACNET_BACKUP_RESTORE + ) + endif() +endfunction() + +# Register a CTest build/run pair for one device test binary. +function( + add_device_ctest + target_name + test_name) + add_test(NAME build_${test_name} COMMAND ${CMAKE_COMMAND} --build "${CMAKE_BINARY_DIR}" --config $ --target + ${target_name}) + add_test(NAME test_${test_name} COMMAND $) + set_tests_properties(test_${test_name} PROPERTIES FIXTURES_REQUIRED fixture_${test_name}) + set_tests_properties(build_${test_name} PROPERTIES FIXTURES_SETUP fixture_${test_name}) +endfunction() + +add_executable(${PROJECT_NAME} + ${TEST_DEVICE_SOURCES} + ) +configure_device_test_target(${PROJECT_NAME} OFF OFF) + +# Backup/Restore-only test binary. +add_executable(${PROJECT_NAME}_backup_restore ${TEST_DEVICE_SOURCES}) +configure_device_test_target( + ${PROJECT_NAME}_backup_restore + OFF + ON) + +# BAC_ROUTING-only test binary. +add_executable(${PROJECT_NAME}_bac_routing ${TEST_DEVICE_SOURCES}) +configure_device_test_target( + ${PROJECT_NAME}_bac_routing + ON + OFF) + +# BAC_ROUTING with Backup/Restore test binary. +add_executable(${PROJECT_NAME}_bac_routing_backup_restore ${TEST_DEVICE_SOURCES}) +configure_device_test_target( + ${PROJECT_NAME}_bac_routing_backup_restore + ON + ON) + +# CTest pair for the Backup/Restore-only binary. +add_device_ctest(${PROJECT_NAME}_backup_restore ${basename}_backup_restore) +# CTest pair for the BAC_ROUTING-only binary. +add_device_ctest(${PROJECT_NAME}_bac_routing ${basename}_bac_routing) +# CTest pair for the BAC_ROUTING with Backup/Restore binary. +add_device_ctest(${PROJECT_NAME}_bac_routing_backup_restore ${basename}_bac_routing_backup_restore) diff --git a/test/bacnet/basic/object/device/src/main.c b/test/bacnet/basic/object/device/src/main.c index 8a735211..6aaab10c 100644 --- a/test/bacnet/basic/object/device/src/main.c +++ b/test/bacnet/basic/object/device/src/main.c @@ -91,6 +91,49 @@ static bool Write_Property_Proprietary(BACNET_WRITE_PROPERTY_DATA *data) return status; } +#if defined(BAC_ROUTING) +/** + * @brief Verify routed virtual devices still do not expose DCC. + */ +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(device_tests, test_Routed_Device_DCC_Remains_Blocked) +#else +static void test_Routed_Device_DCC_Remains_Blocked(void) +#endif +{ + BACNET_CHARACTER_STRING object_name = { 0 }; + uint8_t apdu[MAX_APDU] = { 0 }; + uint16_t device_index = 0; + int len = 0; + bool status = false; + + Device_Init(NULL); + Routing_Device_Init(1000); + status = characterstring_init_ansi(&object_name, "Virtual Device"); + zassert_true(status, NULL); + device_index = Add_Routed_Device(1001, &object_name, "Virtual Device"); + zassert_equal(device_index, 1, NULL); + + status = Set_Routed_Device_Object_Index(0); + zassert_true(status, NULL); + len = Routed_Device_Service_Approval( + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0); + zassert_equal(len, 0, NULL); + + status = Set_Routed_Device_Object_Index(device_index); + zassert_true(status, NULL); + len = Routed_Device_Service_Approval( + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0); + zassert_not_equal(len, 0, NULL); + + len = Routed_Device_Service_Approval( + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, apdu, 1); + zassert_true(len > 0, NULL); + status = Set_Routed_Device_Object_Index(0); + zassert_true(status, NULL); +} +#endif + /** * @brief ReadProperty handler for this objects proprietary properties. * For the given ReadProperty data, the application_data is loaded @@ -421,6 +464,190 @@ static void testDevice(void) return; } + +#if defined(BAC_ROUTING) +static void test_Routed_Device_Reinitialize_Unsupported_State( + BACNET_REINITIALIZED_STATE state) +{ + bool status = false; + BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 }; + + Device_Reinitialize_State_Set(BACNET_REINIT_IDLE); + Device_Reinitialize_Password_Set(NULL); + rd_data.error_class = ERROR_CLASS_DEVICE; + rd_data.error_code = ERROR_CODE_SUCCESS; + rd_data.state = state; + characterstring_init_ansi(&rd_data.password, NULL); + + status = Device_Reinitialize(&rd_data); + zassert_false(status, NULL); + zassert_equal( + rd_data.error_class, ERROR_CLASS_SERVICES, "error-class=%s", + bactext_error_class_name(rd_data.error_class)); + zassert_equal( + rd_data.error_code, ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED, + "error-code=%s", bactext_error_code_name(rd_data.error_code)); + zassert_equal(Device_Reinitialized_State(), BACNET_REINIT_IDLE, NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(device_tests, test_Routed_Device_Reinitialize) +#else +static void test_Routed_Device_Reinitialize(void) +#endif +{ + bool status = false; + BACNET_CHARACTER_STRING object_name = { 0 }; + BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 }; + + Device_Init(NULL); + Routing_Device_Init(100); + characterstring_init_ansi(&object_name, "VirtualDevice"); + zassert_equal(Add_Routed_Device(101, &object_name, "Virtual"), 1, NULL); + + status = Set_Routed_Device_Object_Index(0); + zassert_true(status, NULL); + zassert_equal( + Routed_Device_Service_Approval( + SERVICE_SUPPORTED_REINITIALIZE_DEVICE, 0, NULL, 0), + 0, NULL); + zassert_equal( + Routed_Device_Service_Approval( + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0), + 0, NULL); + Device_Reinitialize_State_Set(BACNET_REINIT_IDLE); + Device_Reinitialize_Password_Set(NULL); + rd_data.error_class = ERROR_CLASS_DEVICE; + rd_data.error_code = ERROR_CODE_SUCCESS; + rd_data.state = BACNET_REINIT_COLDSTART; + characterstring_init_ansi(&rd_data.password, NULL); + status = Device_Reinitialize(&rd_data); + zassert_true(status, NULL); + zassert_equal(Device_Reinitialized_State(), BACNET_REINIT_COLDSTART, NULL); + + status = Set_Routed_Device_Object_Index(1); + zassert_true(status, NULL); + zassert_equal( + Routed_Device_Service_Approval( + SERVICE_SUPPORTED_REINITIALIZE_DEVICE, 0, NULL, 0), + 0, NULL); + zassert_not_equal( + Routed_Device_Service_Approval( + SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, 0, NULL, 0), + 0, NULL); + test_Routed_Device_Reinitialize_Unsupported_State(BACNET_REINIT_COLDSTART); + test_Routed_Device_Reinitialize_Unsupported_State(BACNET_REINIT_WARMSTART); + test_Routed_Device_Reinitialize_Unsupported_State( + BACNET_REINIT_ACTIVATE_CHANGES); +} +#endif + +#if defined(BAC_ROUTING) && defined(BACNET_BACKUP_RESTORE) +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(device_tests, test_Routed_Device_Backup_Restore_Independence) +#else +static void test_Routed_Device_Backup_Restore_Independence(void) +#endif +{ + bool status = false; + BACNET_CHARACTER_STRING object_name = { 0 }; + BACNET_REINITIALIZE_DEVICE_DATA rd_data = { 0 }; + + Device_Init(NULL); + Routing_Device_Init(200); + characterstring_init_ansi(&object_name, "VirtualDevice1"); + zassert_equal(Add_Routed_Device(201, &object_name, "Virtual1"), 1, NULL); + characterstring_init_ansi(&object_name, "VirtualDevice2"); + zassert_equal(Add_Routed_Device(202, &object_name, "Virtual2"), 2, NULL); + + rd_data.error_class = ERROR_CLASS_DEVICE; + rd_data.error_code = ERROR_CODE_SUCCESS; + rd_data.state = BACNET_REINIT_STARTRESTORE; + characterstring_init_ansi(&rd_data.password, NULL); + + Set_Routed_Device_Object_Index(1); + Device_Reinitialize_Password_Set(NULL); + status = Device_Reinitialize(&rd_data); + zassert_true(status, NULL); + zassert_equal( + Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE, + NULL); + + rd_data.error_class = ERROR_CLASS_DEVICE; + rd_data.error_code = ERROR_CODE_SUCCESS; + status = Device_Reinitialize(&rd_data); + zassert_false(status, NULL); + zassert_equal(rd_data.error_class, ERROR_CLASS_DEVICE, NULL); + zassert_equal( + rd_data.error_code, ERROR_CODE_CONFIGURATION_IN_PROGRESS, NULL); + + Set_Routed_Device_Object_Index(0); + zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL); + Set_Routed_Device_Object_Index(2); + Device_Reinitialize_Password_Set(NULL); + zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL); + status = Device_Reinitialize(&rd_data); + zassert_true(status, NULL); + zassert_equal( + Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE, + NULL); + + Set_Routed_Device_Object_Index(1); + rd_data.error_class = ERROR_CLASS_DEVICE; + rd_data.error_code = ERROR_CODE_SUCCESS; + rd_data.state = BACNET_REINIT_ABORTRESTORE; + status = Device_Reinitialize(&rd_data); + zassert_true(status, NULL); + zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL); + + Set_Routed_Device_Object_Index(2); + zassert_equal( + Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_RESTORE, + NULL); +} + +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(device_tests, test_Routed_Device_Backup_Countdown_Per_Device) +#else +static void test_Routed_Device_Backup_Countdown_Per_Device(void) +#endif +{ + bool status = false; + BACNET_CHARACTER_STRING object_name = { 0 }; + + Device_Init(NULL); + Routing_Device_Init(300); + characterstring_init_ansi(&object_name, "VirtualDevice1"); + zassert_equal(Add_Routed_Device(301, &object_name, "Virtual1"), 1, NULL); + characterstring_init_ansi(&object_name, "VirtualDevice2"); + zassert_equal(Add_Routed_Device(302, &object_name, "Virtual2"), 2, NULL); + + status = Set_Routed_Device_Object_Index(1); + zassert_true(status, NULL); + Device_Backup_Failure_Timeout_Set(1); + Device_Backup_And_Restore_State_Set(BACKUP_STATE_PERFORMING_A_BACKUP); + Device_Backup_Failure_Timeout_Restart(); + + status = Set_Routed_Device_Object_Index(2); + zassert_true(status, NULL); + zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL); + + status = Set_Routed_Device_Object_Index(1); + zassert_true(status, NULL); + Device_Timer(500); + zassert_equal( + Device_Backup_And_Restore_State(), BACKUP_STATE_PERFORMING_A_BACKUP, + NULL); + + Device_Timer(500); + zassert_equal( + Device_Backup_And_Restore_State(), BACKUP_STATE_BACKUP_FAILURE, NULL); + + status = Set_Routed_Device_Object_Index(2); + zassert_true(status, NULL); + zassert_equal(Device_Backup_And_Restore_State(), BACKUP_STATE_IDLE, NULL); +} +#endif /** * @} */ @@ -430,9 +657,25 @@ ZTEST_SUITE(device_tests, NULL, NULL, NULL, NULL, NULL); #else void test_main(void) { +#if defined(BAC_ROUTING) && defined(BACNET_BACKUP_RESTORE) + ztest_test_suite( + device_tests, ztest_unit_test(testDevice), + ztest_unit_test(test_Device_Data_Sharing), + ztest_unit_test(test_Routed_Device_DCC_Remains_Blocked), + ztest_unit_test(test_Routed_Device_Reinitialize), + ztest_unit_test(test_Routed_Device_Backup_Restore_Independence), + ztest_unit_test(test_Routed_Device_Backup_Countdown_Per_Device)); +#elif defined(BAC_ROUTING) + ztest_test_suite( + device_tests, ztest_unit_test(testDevice), + ztest_unit_test(test_Device_Data_Sharing), + ztest_unit_test(test_Routed_Device_DCC_Remains_Blocked), + ztest_unit_test(test_Routed_Device_Reinitialize)); +#else ztest_test_suite( device_tests, ztest_unit_test(testDevice), ztest_unit_test(test_Device_Data_Sharing)); +#endif ztest_run_test_suite(device_tests); }