feat: introduce the network state model and secure persistent storage
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
# without default 'CMakeLists.txt' file.
|
||||
|
||||
idf_component_register(
|
||||
SRCS "main.c" "application.c" "command_queue.c" "fleet_generator.c" "game_engine.c" "bot_player.c" "session_manager.c" "game_lifecycle.c" "state_presenter.c" "http_api.c" "sync_service.c"
|
||||
SRCS "main.c" "application.c" "command_queue.c" "fleet_generator.c" "game_engine.c" "bot_player.c" "session_manager.c" "game_lifecycle.c" "state_presenter.c" "http_api.c" "sync_service.c" "network_state.c" "network_credentials.c" "network_credential_store.c"
|
||||
INCLUDE_DIRS "../include"
|
||||
REQUIRES esp_event esp_http_server esp_netif esp_wifi esp_littlefs nvs_flash
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "network_credential_store.h"
|
||||
#include "network_credentials.h"
|
||||
#include "nvs.h"
|
||||
|
||||
static const char *const kNamespace = "network";
|
||||
static const char *const kProfileKey = "profile";
|
||||
bool network_credential_store_load(network_profile_t *profile) { nvs_handle_t handle; network_credential_record_t record; size_t size = sizeof(record); if (nvs_open(kNamespace, NVS_READONLY, &handle) != ESP_OK) return false; const esp_err_t result = nvs_get_blob(handle, kProfileKey, &record, &size); nvs_close(handle); return result == ESP_OK && size == sizeof(record) && network_credential_decode(&record, profile); }
|
||||
bool network_credential_store_replace(const network_profile_t *profile) { network_credential_record_t record; nvs_handle_t handle; if (!network_credential_encode(profile, &record) || nvs_open(kNamespace, NVS_READWRITE, &handle) != ESP_OK) return false; const esp_err_t result = nvs_set_blob(handle, kProfileKey, &record, sizeof(record)); const bool ok = result == ESP_OK && nvs_commit(handle) == ESP_OK; nvs_close(handle); return ok; }
|
||||
bool network_credential_store_delete(void) { nvs_handle_t handle; if (nvs_open(kNamespace, NVS_READWRITE, &handle) != ESP_OK) return false; const esp_err_t result = nvs_erase_key(handle, kProfileKey); const bool ok = (result == ESP_OK || result == ESP_ERR_NVS_NOT_FOUND) && nvs_commit(handle) == ESP_OK; nvs_close(handle); return ok; }
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "network_credentials.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
enum { kCredentialMagic = 0x42534E57U, kCredentialVersion = 1U };
|
||||
static uint32_t checksum(const uint8_t *bytes, size_t length) { uint32_t value = 2166136261U; for (size_t i = 0; i < length; ++i) value = (value ^ bytes[i]) * 16777619U; return value; }
|
||||
bool network_credential_encode(const network_profile_t *profile, network_credential_record_t *record) { if (!network_profile_valid(profile) || record == NULL) return false; memset(record, 0, sizeof(*record)); record->magic = kCredentialMagic; record->version = kCredentialVersion; record->profile = *profile; record->checksum = checksum((const uint8_t *)record, offsetof(network_credential_record_t, checksum)); return true; }
|
||||
bool network_credential_decode(const network_credential_record_t *record, network_profile_t *profile) { if (record == NULL || profile == NULL || record->magic != kCredentialMagic || record->version != kCredentialVersion || record->checksum != checksum((const uint8_t *)record, offsetof(network_credential_record_t, checksum)) || !network_profile_valid(&record->profile)) return false; *profile = record->profile; return true; }
|
||||
bool network_credential_load(const network_credential_backend_t *backend, network_profile_t *profile) { network_credential_record_t record; return backend != NULL && backend->read != NULL && backend->read(backend->context, &record) && network_credential_decode(&record, profile); }
|
||||
bool network_credential_replace(const network_credential_backend_t *backend, const network_profile_t *profile) { network_credential_record_t record; return backend != NULL && backend->write != NULL && network_credential_encode(profile, &record) && backend->write(backend->context, &record); }
|
||||
bool network_credential_delete(const network_credential_backend_t *backend) { return backend != NULL && backend->remove != NULL && backend->remove(backend->context); }
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "network_state.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
bool network_profile_valid(const network_profile_t *profile) {
|
||||
return profile != NULL && profile->ssid[0] != '\0' && memchr(profile->ssid, '\0', sizeof(profile->ssid)) != NULL &&
|
||||
memchr(profile->password, '\0', sizeof(profile->password)) != NULL;
|
||||
}
|
||||
|
||||
network_action_t network_manager_init(network_manager_t *manager, const network_profile_t *profile, uint32_t now_ms) {
|
||||
memset(manager, 0, sizeof(*manager));
|
||||
if (!network_profile_valid(profile)) { manager->state = NETWORK_STATE_FALLBACK; return NETWORK_ACTION_FALLBACK; }
|
||||
manager->profile = *profile; manager->has_profile = true; manager->state = NETWORK_STATE_CONNECTING; manager->started_ms = now_ms;
|
||||
return NETWORK_ACTION_CONNECT;
|
||||
}
|
||||
network_action_t network_manager_tick(network_manager_t *manager, uint32_t now_ms) {
|
||||
if (manager->state == NETWORK_STATE_CONNECTING && (uint32_t)(now_ms - manager->started_ms) >= kNetworkConnectWindowMs) { manager->state = NETWORK_STATE_FALLBACK; return NETWORK_ACTION_FALLBACK; }
|
||||
return NETWORK_ACTION_NONE;
|
||||
}
|
||||
network_action_t network_manager_connected(network_manager_t *manager) { manager->state = NETWORK_STATE_EXTERNAL; return NETWORK_ACTION_NONE; }
|
||||
network_action_t network_manager_disconnected(network_manager_t *manager, uint32_t now_ms) {
|
||||
if (!manager->has_profile) { manager->state = NETWORK_STATE_FALLBACK; return NETWORK_ACTION_FALLBACK; }
|
||||
manager->state = NETWORK_STATE_CONNECTING; manager->started_ms = now_ms; return NETWORK_ACTION_CONNECT;
|
||||
}
|
||||
bool network_manager_begin_validation(network_manager_t *manager, const network_profile_t *candidate) {
|
||||
if (!network_profile_valid(candidate) || manager->state == NETWORK_STATE_VALIDATING) return false;
|
||||
manager->candidate = *candidate; manager->state_before_validation = manager->state; manager->state = NETWORK_STATE_VALIDATING; return true;
|
||||
}
|
||||
network_action_t network_manager_finish_validation(network_manager_t *manager, bool success, uint32_t now_ms) {
|
||||
if (manager->state != NETWORK_STATE_VALIDATING) return NETWORK_ACTION_NONE;
|
||||
if (!success) { manager->state = manager->state_before_validation; return NETWORK_ACTION_NONE; }
|
||||
manager->profile = manager->candidate; manager->has_profile = true; manager->state = NETWORK_STATE_CONNECTING; manager->started_ms = now_ms; return NETWORK_ACTION_CONNECT;
|
||||
}
|
||||
Reference in New Issue
Block a user