fix(app-runtime): align runtime headers with implementation and restore PIO build
Unify AppRegistry/AppRuntimeManager APIs with current source, keep legacy workbench compatibility, and remove conflicting include causing IAppModule redefinition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
// app_registry.h - App catalog loaded from registry.json.
|
||||
// app_registry.h - app catalog registry with legacy AppEntry compatibility.
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "app/app_runtime_types.h"
|
||||
|
||||
class StorageManager;
|
||||
|
||||
struct AppEntry {
|
||||
char id[24] = {0};
|
||||
char title[32] = {0};
|
||||
@@ -10,37 +18,37 @@ struct AppEntry {
|
||||
char icon_path[48] = {0};
|
||||
char entry_screen[40] = {0};
|
||||
bool enabled = true;
|
||||
uint8_t required_capabilities = 0U; // bitmask (lightweight)
|
||||
uint32_t required_capabilities = 0U;
|
||||
};
|
||||
|
||||
// Capability bits for required_capabilities bitmask.
|
||||
namespace AppCap {
|
||||
constexpr uint8_t kAudioOut = 0x01U;
|
||||
constexpr uint8_t kAudioIn = 0x02U;
|
||||
constexpr uint8_t kStorageFs = 0x04U;
|
||||
constexpr uint8_t kStorageSd = 0x08U;
|
||||
constexpr uint8_t kCamera = 0x10U;
|
||||
constexpr uint8_t kWifi = 0x20U;
|
||||
constexpr uint8_t kGpuUi = 0x40U;
|
||||
} // namespace AppCap
|
||||
|
||||
class AppRegistry {
|
||||
public:
|
||||
static constexpr uint8_t kMaxApps = 24U;
|
||||
|
||||
AppRegistry() = default;
|
||||
|
||||
bool loadFromJson(const char* json_path);
|
||||
// New API used by runtime manager / web contracts.
|
||||
bool loadFromFs(const StorageManager& storage, const char* registry_path = "/apps/registry.json");
|
||||
bool loadFromJson(const char* json_text);
|
||||
bool loadFromBuffer(const char* json_buffer, size_t len);
|
||||
|
||||
const AppDescriptor* find(const char* id) const;
|
||||
std::vector<AppDescriptor> listByCategory(const char* category) const;
|
||||
const std::vector<AppDescriptor>& descriptors() const;
|
||||
|
||||
// Legacy API used by workbench UI modules.
|
||||
uint8_t count() const { return count_; }
|
||||
const AppEntry* entry(uint8_t index) const;
|
||||
const AppEntry* findById(const char* id) const;
|
||||
|
||||
uint8_t enabledCount() const;
|
||||
const AppEntry* enabledEntry(uint8_t visible_index) const;
|
||||
|
||||
private:
|
||||
AppEntry entries_[kMaxApps];
|
||||
void loadFallbackCatalog();
|
||||
static uint32_t parseCapabilityMask(const char* csv_caps);
|
||||
void rebuildLegacyEntries();
|
||||
|
||||
std::vector<AppDescriptor> descriptors_;
|
||||
AppEntry entries_[kMaxApps] = {};
|
||||
uint8_t count_ = 0U;
|
||||
};
|
||||
|
||||
@@ -1,67 +1,38 @@
|
||||
// app_runtime_manager.h - App lifecycle: open/close/action state machine.
|
||||
// app_runtime_manager.h - app lifecycle orchestration.
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "app/app_module.h"
|
||||
#include "app/app_registry.h"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
struct RuntimeServices;
|
||||
#include "app/app_registry.h"
|
||||
#include "app/app_runtime_types.h"
|
||||
|
||||
class AppRuntimeManager {
|
||||
public:
|
||||
enum class State : uint8_t {
|
||||
kIdle = 0,
|
||||
kStarting,
|
||||
kRunning,
|
||||
kClosing,
|
||||
kError,
|
||||
};
|
||||
|
||||
struct Snapshot {
|
||||
State state = State::kIdle;
|
||||
const char* app_id = "";
|
||||
const char* entry_screen = "";
|
||||
const char* last_error = "";
|
||||
uint32_t opened_at_ms = 0U;
|
||||
};
|
||||
|
||||
static constexpr uint8_t kMaxModules = 24U;
|
||||
|
||||
AppRuntimeManager() = default;
|
||||
|
||||
void begin(AppRegistry* registry, RuntimeServices* services);
|
||||
// New runtime API.
|
||||
void configure(AppRegistry* registry, const AppContext& context);
|
||||
bool startApp(const AppStartRequest& request, uint32_t now_ms);
|
||||
bool stopApp(const AppStopRequest& request, uint32_t now_ms);
|
||||
bool handleAction(const AppAction& action, uint32_t now_ms);
|
||||
void tick(uint32_t now_ms);
|
||||
AppRuntimeStatus current() const;
|
||||
const AppDescriptor* currentDescriptor() const;
|
||||
|
||||
// Register a module (call during setup, before any open).
|
||||
bool registerModule(IAppModule* module);
|
||||
|
||||
// App lifecycle.
|
||||
// Legacy compatibility API (used by workbench shell).
|
||||
bool open(const char* app_id, const char* mode, uint32_t now_ms);
|
||||
bool close(const char* reason, uint32_t now_ms);
|
||||
bool action(const char* action_name, const char* payload);
|
||||
void tick(uint32_t now_ms);
|
||||
|
||||
// Status.
|
||||
State state() const { return state_; }
|
||||
Snapshot snapshot() const;
|
||||
bool isRunning() const { return state_ == State::kRunning; }
|
||||
bool isIdle() const { return state_ == State::kIdle; }
|
||||
const char* currentAppId() const { return current_app_id_; }
|
||||
bool isRunning() const { return status_.state == AppRuntimeState::kRunning; }
|
||||
bool isIdle() const { return status_.state == AppRuntimeState::kIdle; }
|
||||
|
||||
private:
|
||||
IAppModule* findModule(const char* app_id) const;
|
||||
void transitionTo(State next, const char* reason);
|
||||
uint32_t evaluateMissingCapabilities(const AppDescriptor& descriptor) const;
|
||||
|
||||
AppRegistry* registry_ = nullptr;
|
||||
RuntimeServices* services_ = nullptr;
|
||||
|
||||
IAppModule* modules_[kMaxModules] = {nullptr};
|
||||
uint8_t module_count_ = 0U;
|
||||
|
||||
IAppModule* active_module_ = nullptr;
|
||||
State state_ = State::kIdle;
|
||||
char current_app_id_[24] = {0};
|
||||
char current_entry_screen_[40] = {0};
|
||||
char last_error_[64] = {0};
|
||||
uint32_t opened_at_ms_ = 0U;
|
||||
uint32_t close_requested_ms_ = 0U;
|
||||
AppContext context_ = {};
|
||||
std::unique_ptr<IAppModule> module_;
|
||||
const AppDescriptor* current_descriptor_ = nullptr;
|
||||
AppRuntimeStatus status_ = {};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,10 @@
|
||||
#include "app/app_registry.h"
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
#include "core/str_utils.h"
|
||||
#include "storage_manager.h"
|
||||
@@ -102,6 +105,11 @@ bool AppRegistry::loadFromFs(const StorageManager& storage, const char* registry
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AppRegistry::loadFromBuffer(const char* json_buffer, size_t len) {
|
||||
(void)len;
|
||||
return loadFromJson(json_buffer);
|
||||
}
|
||||
|
||||
const AppDescriptor* AppRegistry::find(const char* id) const {
|
||||
if (id == nullptr || id[0] == '\0') return nullptr;
|
||||
for (const AppDescriptor& d : descriptors_) {
|
||||
@@ -182,6 +190,7 @@ bool AppRegistry::loadFromJson(const char* json_text) {
|
||||
|
||||
descriptors_.push_back(d);
|
||||
}
|
||||
rebuildLegacyEntries();
|
||||
return !descriptors_.empty();
|
||||
}
|
||||
|
||||
@@ -208,6 +217,7 @@ void AppRegistry::loadFallbackCatalog() {
|
||||
d.supports_streaming = f.streaming;
|
||||
descriptors_.push_back(d);
|
||||
}
|
||||
rebuildLegacyEntries();
|
||||
}
|
||||
|
||||
uint32_t AppRegistry::parseCapabilityMask(const char* csv_caps) {
|
||||
@@ -234,3 +244,50 @@ uint32_t AppRegistry::parseCapabilityMask(const char* csv_caps) {
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
const AppEntry* AppRegistry::entry(uint8_t index) const {
|
||||
if (index >= count_) return nullptr;
|
||||
return &entries_[index];
|
||||
}
|
||||
|
||||
const AppEntry* AppRegistry::findById(const char* id) const {
|
||||
if (id == nullptr || id[0] == '\0') return nullptr;
|
||||
for (uint8_t i = 0U; i < count_; ++i) {
|
||||
if (core::equalsIgnoreCase(entries_[i].id, id)) return &entries_[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t AppRegistry::enabledCount() const {
|
||||
uint8_t enabled = 0U;
|
||||
for (uint8_t i = 0U; i < count_; ++i) {
|
||||
if (entries_[i].enabled) enabled += 1U;
|
||||
}
|
||||
return enabled;
|
||||
}
|
||||
|
||||
const AppEntry* AppRegistry::enabledEntry(uint8_t visible_index) const {
|
||||
uint8_t seen = 0U;
|
||||
for (uint8_t i = 0U; i < count_; ++i) {
|
||||
if (!entries_[i].enabled) continue;
|
||||
if (seen == visible_index) return &entries_[i];
|
||||
seen += 1U;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AppRegistry::rebuildLegacyEntries() {
|
||||
count_ = 0U;
|
||||
std::memset(entries_, 0, sizeof(entries_));
|
||||
for (const AppDescriptor& d : descriptors_) {
|
||||
if (count_ >= kMaxApps) break;
|
||||
AppEntry& out = entries_[count_++];
|
||||
core::copyText(out.id, sizeof(out.id), d.id);
|
||||
core::copyText(out.title, sizeof(out.title), d.title);
|
||||
core::copyText(out.category, sizeof(out.category), d.category);
|
||||
core::copyText(out.icon_path, sizeof(out.icon_path), d.icon_path);
|
||||
core::copyText(out.entry_screen, sizeof(out.entry_screen), d.entry_screen);
|
||||
out.enabled = d.enabled;
|
||||
out.required_capabilities = d.required_capabilities;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +205,31 @@ void AppRuntimeManager::configure(AppRegistry* registry, const AppContext& conte
|
||||
status_ = {};
|
||||
}
|
||||
|
||||
bool AppRuntimeManager::open(const char* app_id, const char* mode, uint32_t now_ms) {
|
||||
AppStartRequest request = {};
|
||||
core::copyText(request.id, sizeof(request.id), (app_id != nullptr) ? app_id : "");
|
||||
core::copyText(request.mode, sizeof(request.mode), (mode != nullptr) ? mode : "default");
|
||||
core::copyText(request.source, sizeof(request.source), "workbench");
|
||||
return startApp(request, now_ms);
|
||||
}
|
||||
|
||||
bool AppRuntimeManager::close(const char* reason, uint32_t now_ms) {
|
||||
AppStopRequest request = {};
|
||||
core::copyText(request.id, sizeof(request.id), status_.id);
|
||||
core::copyText(request.reason, sizeof(request.reason), (reason != nullptr) ? reason : "api");
|
||||
return stopApp(request, now_ms);
|
||||
}
|
||||
|
||||
bool AppRuntimeManager::action(const char* action_name, const char* payload) {
|
||||
AppAction action_request = {};
|
||||
core::copyText(action_request.id, sizeof(action_request.id), status_.id);
|
||||
core::copyText(action_request.name, sizeof(action_request.name),
|
||||
(action_name != nullptr) ? action_name : "");
|
||||
core::copyText(action_request.payload, sizeof(action_request.payload),
|
||||
(payload != nullptr) ? payload : "");
|
||||
return handleAction(action_request, millis());
|
||||
}
|
||||
|
||||
bool AppRuntimeManager::startApp(const AppStartRequest& request, uint32_t now_ms) {
|
||||
if (registry_ == nullptr || request.id[0] == '\0') {
|
||||
core::copyText(status_.last_error, sizeof(status_.last_error), "app_registry_unavailable");
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "app/app_registry.h"
|
||||
#include "runtime/runtime_services.h"
|
||||
|
||||
bool AudioPlayerModule::onOpen(const AppEntry& entry, const char* mode, RuntimeServices* services) {
|
||||
|
||||
Reference in New Issue
Block a user