Add versioned persistent configuration protocol
This commit is contained in:
parent
b11ae076a8
commit
7afc9981fe
36 changed files with 2784 additions and 756 deletions
186
configuration_service.cpp
Normal file
186
configuration_service.cpp
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
#include "configuration_service.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "pico/critical_section.h"
|
||||
#include "pico_configuration_storage.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint32_t kMinimumCommitIntervalMs = 1000;
|
||||
|
||||
critical_section_t g_lock;
|
||||
bool g_prepared = false;
|
||||
ConfigurationStorage g_storage;
|
||||
ConfigurationTransaction g_transaction;
|
||||
ConfigurationServiceSnapshot g_snapshot;
|
||||
bool g_has_committed = false;
|
||||
uint32_t g_last_commit_ms = 0;
|
||||
|
||||
void publish_storage_snapshot(ConfigurationServiceState state) {
|
||||
const ConfigurationStorageSnapshot& stored = g_storage.snapshot();
|
||||
AdapterConfiguration configuration = adapter_configuration_default();
|
||||
if (stored.valid &&
|
||||
(stored.schema_version != ADAPTER_CONFIGURATION_SCHEMA_VERSION ||
|
||||
!adapter_configuration_decode(stored.payload,
|
||||
stored.payload_size,
|
||||
&configuration))) {
|
||||
state = ConfigurationServiceState::kStorageError;
|
||||
}
|
||||
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
g_snapshot.state = state;
|
||||
g_snapshot.configuration = configuration;
|
||||
g_snapshot.generation = stored.valid ? stored.generation : 0;
|
||||
g_snapshot.payload_crc = stored.valid ? stored.payload_crc : 0;
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void configuration_service_prepare() {
|
||||
if (g_prepared) {
|
||||
return;
|
||||
}
|
||||
critical_section_init(&g_lock);
|
||||
g_snapshot = {};
|
||||
g_snapshot.configuration = adapter_configuration_default();
|
||||
g_transaction.clear();
|
||||
g_prepared = true;
|
||||
}
|
||||
|
||||
void configuration_service_initialize_on_storage_core() {
|
||||
if (!g_prepared) {
|
||||
configuration_service_prepare();
|
||||
}
|
||||
const bool initialized =
|
||||
g_storage.initialize(pico_configuration_storage_io());
|
||||
publish_storage_snapshot(initialized
|
||||
? ConfigurationServiceState::kReady
|
||||
: ConfigurationServiceState::kStorageError);
|
||||
}
|
||||
|
||||
void configuration_service_task_on_storage_core(uint32_t now_ms) {
|
||||
uint8_t payload[CONFIGURATION_STORAGE_MAX_PAYLOAD_SIZE]{};
|
||||
uint16_t payload_size = 0;
|
||||
uint16_t schema_version = 0;
|
||||
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
const ConfigurationTransactionSnapshot transaction =
|
||||
g_transaction.snapshot();
|
||||
if (transaction.status == ConfigurationTransactionStatus::kPending &&
|
||||
(!g_has_committed ||
|
||||
static_cast<uint32_t>(now_ms - g_last_commit_ms) >=
|
||||
kMinimumCommitIntervalMs)) {
|
||||
payload_size = transaction.expected_size;
|
||||
schema_version = g_transaction.schema_version();
|
||||
memcpy(payload, g_transaction.payload(), payload_size);
|
||||
}
|
||||
critical_section_exit(&g_lock);
|
||||
if (payload_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ConfigurationStorageResult result =
|
||||
g_storage.commit(schema_version, payload, payload_size);
|
||||
const ConfigurationStorageSnapshot& stored = g_storage.snapshot();
|
||||
ConfigurationTransactionStatus transaction_status =
|
||||
ConfigurationTransactionStatus::kStorageError;
|
||||
if (result == ConfigurationStorageResult::kOk) {
|
||||
transaction_status = ConfigurationTransactionStatus::kCommitted;
|
||||
g_has_committed = true;
|
||||
g_last_commit_ms = now_ms;
|
||||
} else if (result == ConfigurationStorageResult::kUnchanged) {
|
||||
transaction_status = ConfigurationTransactionStatus::kUnchanged;
|
||||
}
|
||||
|
||||
AdapterConfiguration configuration{};
|
||||
ConfigurationServiceState service_state =
|
||||
ConfigurationServiceState::kStorageError;
|
||||
if (stored.valid &&
|
||||
stored.schema_version == ADAPTER_CONFIGURATION_SCHEMA_VERSION &&
|
||||
adapter_configuration_decode(stored.payload, stored.payload_size,
|
||||
&configuration)) {
|
||||
service_state = ConfigurationServiceState::kReady;
|
||||
} else if (!stored.valid) {
|
||||
configuration = adapter_configuration_default();
|
||||
}
|
||||
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
g_transaction.set_result(transaction_status,
|
||||
stored.valid ? stored.generation : 0,
|
||||
stored.valid ? stored.payload_crc : 0);
|
||||
g_snapshot.state = service_state;
|
||||
g_snapshot.configuration = configuration;
|
||||
g_snapshot.generation = stored.valid ? stored.generation : 0;
|
||||
g_snapshot.payload_crc = stored.valid ? stored.payload_crc : 0;
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
}
|
||||
|
||||
ConfigurationTransactionStatus configuration_service_begin(
|
||||
uint32_t transaction_id, uint16_t schema_version, size_t payload_size,
|
||||
uint32_t payload_crc) {
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
const ConfigurationTransactionStatus status = g_transaction.begin(
|
||||
transaction_id, schema_version, payload_size, payload_crc);
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
ConfigurationTransactionStatus configuration_service_append(
|
||||
uint32_t transaction_id, size_t offset, const uint8_t* data,
|
||||
size_t size) {
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
const ConfigurationTransactionStatus status =
|
||||
g_transaction.append(transaction_id, offset, data, size);
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
ConfigurationTransactionStatus configuration_service_commit(
|
||||
uint32_t transaction_id) {
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
const ConfigurationTransactionStatus status =
|
||||
g_transaction.finish(transaction_id);
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
ConfigurationTransactionStatus configuration_service_reset(
|
||||
uint32_t transaction_id) {
|
||||
uint8_t payload[ADAPTER_CONFIGURATION_ENCODED_SIZE]{};
|
||||
const AdapterConfiguration defaults = adapter_configuration_default();
|
||||
if (!adapter_configuration_encode(defaults, payload, sizeof(payload))) {
|
||||
return ConfigurationTransactionStatus::kMalformed;
|
||||
}
|
||||
const uint32_t crc = configuration_crc32(payload, sizeof(payload));
|
||||
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
ConfigurationTransactionStatus status = g_transaction.begin(
|
||||
transaction_id, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
||||
sizeof(payload), crc);
|
||||
if (status == ConfigurationTransactionStatus::kReceiving) {
|
||||
status = g_transaction.append(transaction_id, 0, payload,
|
||||
sizeof(payload));
|
||||
}
|
||||
if (status == ConfigurationTransactionStatus::kReceiving) {
|
||||
status = g_transaction.finish(transaction_id);
|
||||
}
|
||||
g_snapshot.transaction = g_transaction.snapshot();
|
||||
critical_section_exit(&g_lock);
|
||||
return status;
|
||||
}
|
||||
|
||||
void configuration_service_snapshot(ConfigurationServiceSnapshot* output) {
|
||||
if (output == nullptr) {
|
||||
return;
|
||||
}
|
||||
critical_section_enter_blocking(&g_lock);
|
||||
*output = g_snapshot;
|
||||
critical_section_exit(&g_lock);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue