#ifndef SETTINGS_BACKUP_H #define SETTINGS_BACKUP_H /* Переносимое ядро резервирования не включает HAL, FatFs, AppStorage и Modbus. */ #include #include #define SETTINGS_BACKUP_SCHEMA_VERSION 1U #define SETTINGS_BACKUP_MAX_ROOMS 32U #define SETTINGS_BACKUP_MAX_SENSORS 32U #define SETTINGS_BACKUP_MAX_PATH 160U /* Календарь передаётся приложением вместе с признаком доверия к RTC. */ typedef struct { uint16_t year; uint8_t month; uint8_t date; uint8_t hours; uint8_t minutes; uint8_t seconds; uint8_t valid; } SettingsBackupDateTime; /* Одна комната содержит только документированные инженерные параметры. */ typedef struct { uint16_t setpoint_x10; uint8_t hysteresis_x10; uint8_t calibration_start_pct; uint16_t full_open_time_100ms; uint8_t target_position_pct; uint8_t confirmed_position_pct; } SettingsBackupRoom; /* В JSON всегда попадает полный ROM, включая family и Dallas CRC. */ typedef struct { uint8_t rom[8]; uint8_t room; } SettingsBackupSensor; /* Снимок создаётся адаптером только из committed-записи AppStorage. */ typedef struct { uint32_t revision; uint32_t content_crc32; uint8_t requested_backend; uint8_t active_backend; uint8_t room_count; uint8_t sensor_count; SettingsBackupDateTime rtc; SettingsBackupRoom rooms[SETTINGS_BACKUP_MAX_ROOMS]; SettingsBackupSensor sensors[SETTINGS_BACKUP_MAX_SENSORS]; } SettingsBackupSnapshot; /* Короткий набор результатов позволяет порту отобразить SD absent/full/I/O. */ typedef enum { SETTINGS_BACKUP_PORT_OK = 0, SETTINGS_BACKUP_PORT_NOT_READY, SETTINGS_BACKUP_PORT_NO_SPACE, SETTINGS_BACKUP_PORT_IO_ERROR } SettingsBackupPortResult; /* Наблюдаемое состояние не скрывает retry и отказ носителя от Modbus/GUI. */ typedef enum { SETTINGS_BACKUP_IDLE = 0, SETTINGS_BACKUP_PENDING, SETTINGS_BACKUP_LAST_OK, SETTINGS_BACKUP_LAST_NOT_READY, SETTINGS_BACKUP_LAST_NO_SPACE, SETTINGS_BACKUP_LAST_IO_ERROR, SETTINGS_BACKUP_LAST_INVALID_SNAPSHOT } SettingsBackupStatus; /* Все операции платформы ограничены одной файловой транзакцией за вызов. */ typedef struct { uint32_t (*tick_ms)(void *context); SettingsBackupPortResult (*mount)(void *context); void (*unmount)(void *context); SettingsBackupPortResult (*find_identical)(void *context, uint32_t revision, uint32_t crc32, uint8_t *found); SettingsBackupPortResult (*resolve_paths)(void *context, const SettingsBackupSnapshot *snapshot, char *final_path, uint32_t final_capacity, char *temporary_path, uint32_t temporary_capacity); SettingsBackupPortResult (*atomic_write)(void *context, const char *temporary_path, const char *final_path, const char *utf8_json, uint32_t length); SettingsBackupPortResult (*apply_retention)(void *context, uint16_t maximum_copies); } SettingsBackupPort; /* Контекст хранит pending-снимок: вызывающий может освобождать свой буфер. */ typedef struct { SettingsBackupPort port; void *port_context; SettingsBackupSnapshot pending_snapshot; char *json_buffer; uint32_t json_capacity; char final_path[SETTINGS_BACKUP_MAX_PATH]; char temporary_path[SETTINGS_BACKUP_MAX_PATH]; uint32_t next_retry_ms; uint16_t maximum_copies; uint8_t phase; uint8_t retry_count; uint8_t pending; SettingsBackupStatus status; } SettingsBackup; /* Инициализация не обращается к SD и потому безопасна во время старта МК. */ uint8_t SettingsBackup_Init(SettingsBackup *instance, const SettingsBackupPort *port, void *port_context, char *json_buffer, uint32_t json_capacity, uint16_t maximum_copies); /* Новая committed-ревизия заменяет только ещё не записанный pending-снимок. */ uint8_t SettingsBackup_Notify(SettingsBackup *instance, const SettingsBackupSnapshot *snapshot); /* Service делает не более одного mount/scan/write/retention шага за проход. */ void SettingsBackup_Service(SettingsBackup *instance); /* Форматирование и CRC открыты для host-тестов без файловой системы. */ uint32_t SettingsBackup_CalculateContentCrc(const SettingsBackupSnapshot *snapshot); int SettingsBackup_FormatJson(const SettingsBackupSnapshot *snapshot, char *output, uint32_t capacity); SettingsBackupStatus SettingsBackup_GetStatus(const SettingsBackup *instance); #endif /* SETTINGS_BACKUP_H */