Files
roro9stack/src/services/storage_service.h
T
twislaandClaude Opus 5.5 21b3d9e422 OTA steps 3-5: Update Service, Probation and Rollback, Update from SD
- EcdsaVerifier (mbedTLS, embedded public key) and EspOtaSink (writes
  the inactive app slot, esp_ota_end validates the image, then sets
  the boot partition)
- UpdateService: listens on TCP 3232 (and mDNS roro9stack-<id>) while
  Wi-Fi is Connected; streams into UpdateParser; replies OK/ERR to the
  sender; remembers the pending version so a Rollback is reported
  after the reboot
- Probation (host-tested): confirm after the first frame + 30 s + Wi-Fi
  (if configured); roll back if configured Wi-Fi never connects in 3 min
- Main loop: full-screen progress while receiving; restart once
  installed, waiting up to 60 s for Text Entry to end
- Settings > Firmware: version, Probation status, push address and
  name, and the .ota files in /updates on the SD card to install
- StorageService.runJob() runs work on the storage task (SD installs)
- wifi status prints IP and running version; RORO_TEST_CRASH test hook

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-03 21:35:33 +02:00

83 lines
2.8 KiB
C++

#pragma once
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#include <deque>
#include <functional>
#include <string>
#include <utility>
#include <vector>
#include "cleanup_plan.h"
#include "service.h"
#include "storage_monitor.h"
namespace roro {
// Owns the microSD card. All card access happens on this Service's own task (the SD driver isn't
// safe across tasks, and a failed mount blocks for seconds): it mounts and watches usage, writes
// queued Log lines in batches, lists files for Storage Clean-up, deletes, and formats.
class StorageService : public Service {
public:
explicit StorageService(EventBus& bus) : monitor_(bus), bus_(bus) {}
const char* name() const override { return "storage"; }
void start() override;
void stop() override;
StorageState state() const;
// Queues one line for a Log file (created with its folders as needed). Dropped while Logs are
// paused (over 90 % full, or no card) or if too much is already waiting.
void appendLine(const std::string& path, const std::string& line);
uint32_t droppedLines() const { return dropped_; }
// Storage Clean-up: list every file of every category, then delete a selection.
void requestListing();
bool listingReady() const;
std::vector<std::vector<StoredFile>> listing() const; // one vector per kCleanupCategories entry
void requestDelete(std::vector<std::string> paths);
// Runs `job` on the storage task, where card access is safe (e.g. reading an Update File).
void runJob(std::function<void()> job);
// Erases the whole card: one partition spanning the card, formatted FAT32.
bool requestFormat();
bool formatting() const { return formatRequested_; }
private:
static constexpr uint32_t kWakeMs = 1000;
static constexpr uint32_t kPollEvery = 15; // wake-ups between usage checks
static constexpr size_t kMaxPendingBytes = 16 * 1024;
static void taskEntry(void* self);
void loop();
void poll();
bool mount();
void format();
void writePending();
void list();
void remove(const std::vector<std::string>& paths);
void lock() const { xSemaphoreTake(lock_, portMAX_DELAY); }
void unlock() const { xSemaphoreGive(lock_); }
StorageMonitor monitor_;
EventBus& bus_;
bool mounted_ = false;
TaskHandle_t task_ = nullptr;
mutable SemaphoreHandle_t lock_ = nullptr;
// Shared with other tasks, guarded by lock_.
std::deque<std::pair<std::string, std::string>> pending_;
size_t pendingBytes_ = 0;
uint32_t dropped_ = 0;
bool listingRequested_ = false;
bool listingReady_ = false;
std::vector<std::vector<StoredFile>> listing_;
std::vector<std::string> toDelete_;
std::vector<std::function<void()>> jobs_;
volatile bool formatRequested_ = false;
};
} // namespace roro