Skip to main content

DataBaseManager

Повна документація класу DataBaseManager з детальним описом усіх публічних, приватних та допоміжних методів.

Клас DataBaseManager (простір імен flowercare::storage) відповідає за...

Публічні методи

DataBaseManager()

DataBaseManager()

Конструктор класу. Створює новий екземпляр та скидає вказівник партиції _part у nullptr.


begin()

bool begin()

Ініціалізує менеджер сховища.

  1. Шукає партицію storage у flash-пам'яті (тип ESP_PARTITION_TYPE_DATA, підтип 0x99).
  2. Валідує стан RTC пам'яті через isRtcStateValid().
  3. Визначає та оновлює rebootId через manageRebootId().
  4. Якщо RTC валідний: відновлює _currentSector та _currentOffset прямо з RTC без звернення до flash.
  5. Якщо RTC невалідний: запускає бінарний пошук findLastWritePositionBinary() по всій партиції, щоб визначити останню позицію запису та обчислити наступні сектор та зсув, після чого оновлює RTC.
  • Повертає: true при успішній знахідці партиції та вирахуванні позиції; false, якщо партиція відсутня.

writeRecord()

bool writeRecord(const uint8_t* sensorData)

Формує та записує новий дельта-запис у flash-пам'ять.

  1. Якщо _currentOffset == 0, виконує прання поточного сектора flash-пам'яті (esp_partition_erase_range).
  2. Створює структуру DataRecord, заповнює її rebootId, поточним штампом часу getTimestamp() та копіює sensorData.
  3. Записує структуру у flash за адресою поточного сектора та зсуву.
  4. Інкрементує _currentOffset. Якщо сектор заповнено, переходить до наступного сектора (з кільцевим поверненням до 0).
  5. Зберігає новий стан у RTC через updateRtcState().
  • Параметри:

  • sensorData — вказівник на масив даних із датчиків розміром DATA_SIZE_WITHOUT_TIMESTAMP.

  • Повертає: true, якщо запис пройшов успішно, інакше false.


findIndexByTargetKey()

uint32_t findIndexByTargetKey(uint32_t targetTimestamp, uint32_t targetRebootId)

Шукає індекс запису в пам'яті за цільовим комбінованим ключем (пара rebootId та timestamp).

  1. Формує 64-бітний ключ (targetRebootId << 32) | targetTimestamp.
  2. Знаходить межі записаних даних у кільцевому буфері (найстаріший firstIdx та найновіший lastIdx).
  3. Виконує послідовне зіставлення ключів від firstIdx до lastIdx.
  • Параметри:

  • targetTimestamp — часовий штамп запису для пошуку.

  • targetRebootId — ID сесії/перезавантаження пристрою.

  • Повертає:

  • firstIdx, якщо ключ менший або дорівнює найстарішому.

  • _totalMaxRecords, якщо ключ більший за останній записаний.

  • Знайдений індекс запису у кільцевому буфері.


getLastRecordIndex()

uint32_t getLastRecordIndex() const

Розраховує абсолютний індекс останнього записаного елемента на основі поточних _currentSector та _currentOffset.

  • Повертає: uint32_t — індекс останнього записаного елемента у діапазоні 0.._totalMaxRecords - 1.

readRecordsPacket()

size_t readRecordsPacket(uint32_t &currentIdx, DataRecord* outBuffer, size_t maxRecordsToRead)

Зчитує пакет із декількох послідовних записів з пам'яті у наданий буфер.

  1. Обчислює фізичну адресу кожного запису за індексом currentIdx.
  2. Зчитує дані з flash-пам'яті в outBuffer.
  3. Зупиняє зчитування, якщо досягнуто неініціалізованої пам'яті (timestamp == 0xFFFFFFFF) або останнього запису lastIdx.
  4. Зміщує значення currentIdx на наступну позицію після кожного прочитаного елемента (якщо досягнуто кінця — встановлює в _totalMaxRecords).
  • Параметри:

  • currentIdx (in/out) — посилання на індекс, з якого починати зчитування. Значення модифікується під час зчитування.

  • outBuffer — вказівник на масив/буфер пам'яті для збереження DataRecord.

  • maxRecordsToRead — максимальна кількість елементів для зчитування.

  • Повертає: size_t — фактично зчитану кількість записів.


Приватні методи

updateRtcState()

void DataBaseManager::updateRtcState()

Оновлює змінні стану у структурі rtcStorageState в RTC SRAM:

  • Записує магічний ключ RTC_MAGIC_KEY.
  • Записує поточні значення _currentSector та _currentOffset.
  • Розраховує контрольну суму CRC32 для всієї структури RTC та зберігає її у rtcStorageState.checksum.

getTimestamp()

uint32_t DataBaseManager::getTimestamp()

Обчислює час у секундах, що минув від моменту старту/перезавантаження пристрою.

  • Використовує низькорівневі функції ESP32 rtc_time_get() та rtc_time_slowclk_to_us() для перетворення тіків RTC-таймера в мікросекунди, а потім у секунди.
  • Повертає: uint32_t — кількість секунд з моменту запуску.

isRtcStateValid() const

bool DataBaseManager::isRtcStateValid() const

Перевіряє цілісність даних у RTC SRAM після перезапуску:

  1. Перевіряє наявність RTC_MAGIC_KEY.
  2. Перевіряє, чи не виходять значення сектора та зсуву за припустимі межі partition layout.
  3. Порівнює збережену чексуму з щойно розрахованою контрольною сумою CRC32.
  • Повертає: true, якщо стан у RTC правильний і йому можна довіряти.

calculateRtcChecksum() const

uint32_t DataBaseManager::calculateRtcChecksum() const

Обчислює CRC32 для вмісту структури FlowerCareRTCData (за винятком самого поля checksum).

  • Повертає: uint32_t — розраховане значення контрольної суми.

manageRebootId()

uint32_t DataBaseManager::manageRebootId(bool rtcValid)

Керує ідентифікатором сесії/перезапуску пристрою (rebootId):

  • Якщо причина скидання — ESP_RST_DEEPSLEEP і стан RTC є валідним, повертає збережений rtcStorageState.rebootId.
  • При холодному старті (Cold Boot) або пошкодженні RTC:
  1. Відкриває NVS через Preferences (простір storage_data).
  2. Інкрементує збережений reboot_id.
  3. Оновлює rtcStorageState.rebootId та фіксує початковий час rtcStorageState.startRtcTicks.
  • Параметри: rtcValid — прапорець валідності даних у RTC.
  • Повертає: uint32_t — активний rebootId.

getCombinedKeyAtIndex()

uint64_t DataBaseManager::getCombinedKeyAtIndex(uint32_t index)

Зчитує з flash-пам'яті перші 8 байт запису за вказаним індексом.

  • Зчитувані 8 байт відповідають полям rebootId (4 байти) та timestamp (4 байти), утворюючи складений 64-бітний ключ.
  • Параметри: index — абсолютний індекс запису у сховищі.
  • Повертає: uint64_t — значення комбінованого ключа, або UINT64_MAX у разі помилки читання або якщо індекс перевищує _totalMaxRecords.

findLastWritePositionBinary()

uint32_t DataBaseManager::findLastWritePositionBinary()

Виконує двопрохідний бінарний пошук по flash-пам'яті для визначення індексу останнього записаного елемента у кільцевому буфері.

  • Враховує два сценарії:
  1. Буфер заповнений частково (пошук межі між записаними даними та незаписаною пам'яттю UINT64_MAX).
  2. Буфер заповнений повністю з інверсією ключів (пошук точки переходу/переповнення кільцевого буфера, де currentKey > nextKey).
  • Повертає: uint32_t — індекс останнього дійсного запису у пам'яті.

Внутрішні утиліти (Anonymous Namespace)

Функції та змінні, що визначені у файлі реалізації .cpp в анонімному просторі імен і недоступні ззовні модуля:

calculate_crc32()

uint32_t calculate_crc32(const uint8_t *data, size_t length)

Реалізація стандартного алгоритму обчислення IEEE 802.3 CRC32 з поліномом 0xEDB88320. Використовується для перевірки цілісності структури стан-RTC.

Локальні змінні

  • RTC_DATA_ATTR flowercare::FlowerCareRTCData rtcStorageState — структура даних у швидкій пам'яті RTC SRAM, яка зберігає свій вміст під час Deep Sleep.
  • Preferences prefs — об'єкт для роботи з енергонезалежною пам'яттю NVS (використовується для збереження reboot_id).

Приклад використання

#include "DataBaseManager.hpp"

void setup() {
Serial.begin(115200);

// 1. Ініціалізація сховища
if (!flowercare::storage::db.begin()) {
Serial.println("Помилка ініціалізації сховища!");
return;
}

// 2. Запис даних з сенсора
uint8_t rawData[12] = {0x01, 0x02, 0x03, 0x04};
flowercare::storage::db.writeRecord(rawData);

// 3. Зчитування останнього запису
uint32_t lastIdx = flowercare::storage::db.getLastRecordIndex();

// 4. Пакетне зчитування
DataRecord buffer[5];
uint32_t readIndex = 0;
size_t readCount = flowercare::storage::db.readRecordsPacket(readIndex, buffer, 5);

Serial.printf("Зчитано записів: %d, Наступний індекс: %u\n", readCount, readIndex);
}