Skip to content

Commit

Permalink
New boot step control
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardtfn committed Nov 29, 2024
1 parent ab2afdf commit 334163e
Show file tree
Hide file tree
Showing 35 changed files with 412 additions and 470 deletions.
157 changes: 115 additions & 42 deletions components/nspanel_ha_blueprint/core_boot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,77 +5,150 @@
#include "core_boot.h" // Include the header file for function and variable declarations.

namespace nspanel_ha_blueprint {
BootStepVector* boot_steps = nullptr;

// Total number of defined boot steps.
uint8_t total_boot_steps = 0;
uint32_t completed_boot_steps = 0;
uint32_t registered_applications = 0;
// Function to set up the boot steps vector
void setup_boot_steps() {
if (!boot_steps) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
esphome::ESP_LOGD("boot_steps", "Allocating memory for boot steps vector in PSRAM");
#endif

// Function to mark a boot step as completed.
bool complete_boot_step(const uint32_t step) {
// Validate that the step is a power of two (i.e., only one bit is set).
if (step == 0 || (step & (step - 1)) != 0) {
return false;
esphome::ExternalRAMAllocator<BootStepVector> vector_allocator(
esphome::ExternalRAMAllocator<BootStepVector>::ALLOW_FAILURE);
boot_steps = vector_allocator.allocate(1);

if (!boot_steps) {
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_ERROR
esphome::ESP_LOGE("boot_steps", "Failed to allocate memory for boot steps vector.");
#endif
return;
}

// Use placement new to initialize the vector in allocated memory
new (boot_steps) BootStepVector(esphome::ExternalRAMAllocator<BootStep>());

#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_DEBUG
esphome::ESP_LOGD("boot_steps", "Memory allocated for boot steps vector in PSRAM");
#endif
}
// Use bitwise OR to set the corresponding bit in completed_boot_steps.
completed_boot_steps |= step;
return true;
}

// Function to register an application for boot.
bool register_application(const uint32_t app_bit) {
// Validate that the app_bit is a power of two (i.e., only one bit is set).
if (app_bit == 0 || (app_bit & (app_bit - 1)) != 0) {
bool register_application(uint8_t id, const char* name) {
if (!boot_steps) {
setup_boot_steps();
}

// Validate that the id is a power of two (i.e., only one bit is set)
if (id == 0 || (id & (id - 1)) != 0) {
return false;
}
// Check if the application is already registered, if not, increment total_boot_steps.
if ((registered_applications & app_bit) == 0) {
total_boot_steps++;
// Check if the application is already registered
for (const auto& step : *boot_steps) {
if (step.id == id) {
return false; // Already registered
}
}
// Use bitwise OR to set the corresponding bit in registered_applications.
registered_applications |= app_bit;
// Add new boot step to the list
boot_steps->emplace_back(id, name);
return true;
}

// Helper function to count the number of bits set to '1' in a uint32_t value.
uint8_t count_bits_set(uint32_t value) {
uint8_t count = 0;
while (value) {
count += value & 1;
value >>= 1;
// Function to mark a boot step as completed.
bool complete_boot_step(uint8_t id) {
for (auto& step : *boot_steps) {
if (step.id == id) {
step.completed = true;
return true;
}
}
return count;
}

// Function to get the number of unique boot steps that have been completed.
uint8_t get_boot_steps_completed() {
return count_bits_set(completed_boot_steps);
return false; // Step not found
}

// Function to calculate the boot progress percentage with rounding.
uint8_t get_boot_progress_percentage() {
if (total_boot_steps == 0) {
if (boot_steps == nullptr || boot_steps->empty()) {
return 0;
}
return static_cast<uint8_t>((get_boot_steps_completed() * 100 + total_boot_steps / 2) / total_boot_steps);
uint8_t completed_steps = 0;
for (const auto& step : *boot_steps) {
if (step.completed) {
completed_steps++;
}
}
return static_cast<uint8_t>((completed_steps * 100 + boot_steps->size() / 2) / boot_steps->size());
}

// Function to check if all registered applications have completed boot.
bool is_boot_complete() {
// Boot is complete if all bits set in registered_applications are also set in completed_boot_steps.
return (registered_applications & completed_boot_steps) == registered_applications;
if (boot_steps == nullptr) {
return false;
}
for (const auto& step : *boot_steps) {
if (!step.completed) {
return false;
}
}
return true;
}

// Function to reset the completed boot steps and registered applications to zero.
// Function to reset the completed boot steps to the initial state.
void reset_boot_steps() {
total_boot_steps = 0;
completed_boot_steps = 0;
registered_applications = 0;
if (boot_steps == nullptr) {
return; // No boot steps to reset
}
for (auto& step : *boot_steps) {
step.completed = false;
}
}

// Function to check if a specific boot step has been completed.
bool is_boot_step_completed(uint32_t step) {
return (completed_boot_steps & step) != 0;
bool is_boot_step_completed(uint8_t id) {
if (boot_steps == nullptr) {
return false;
}
for (const auto& step : *boot_steps) {
if (step.id == id) {
return step.completed;
}
}
return false;
}

// Function to get the total number of registered boot steps
uint8_t get_total_registered_boot_steps() {
if (boot_steps == nullptr) {
return 0;
}
return static_cast<uint8_t>(boot_steps->size());
}

// Function to get the total number of completed boot steps
uint8_t get_completed_boot_steps() {
if (boot_steps == nullptr) {
return 0;
}
uint8_t completed_steps = 0;
for (const auto& step : *boot_steps) {
if (step.completed) {
completed_steps++;
}
}
return completed_steps;
}

// Function to get a pointer to a specific boot step by its ID
BootStep* get_boot_step(uint8_t id) {
if (boot_steps == nullptr) {
return nullptr;
}
for (auto& step : *boot_steps) {
if (step.id == id) {
return &step;
}
}
return nullptr; // Step not found
}

} // namespace nspanel_ha_blueprint
Expand Down
64 changes: 43 additions & 21 deletions components/nspanel_ha_blueprint/core_boot.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,47 @@
#ifdef NSPANEL_HA_BLUEPRINT_CORE_BOOT

#include <stdint.h> // Include standard integer types
#include <string.h> // Include for handling strings
#include <vector> // Include for using vector to store boot steps
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

namespace nspanel_ha_blueprint {
// Total number of defined boot steps.
extern uint8_t total_boot_steps;
extern uint32_t completed_boot_steps;
extern uint32_t registered_applications;
// Structure to define a boot step
struct BootStep {
uint8_t id; // Unique identifier for the boot step (bit to be flagged)
char name[21]; // Name of the boot step (max 20 chars + null terminator)
bool completed = false; // Status indicating if the boot of that item was completed

// Function to mark a boot step as completed.
// Parameters:
// step - The boot step constant representing the step to be marked as completed.
// Returns:
// true if the boot step has been successfully marked completed;
// false otherwise (like in the case of an invalid step constant).
bool complete_boot_step(const uint32_t step);
BootStep(uint8_t id_, const char* name_) : id(id_) {
strncpy(name, name_, sizeof(name) - 1);
name[sizeof(name) - 1] = '\0'; // Ensure null-termination
}
};

// Vector to store the boot steps in PSRAM
using BootStepVector = std::vector<BootStep, esphome::ExternalRAMAllocator<BootStep>>;
extern BootStepVector* boot_steps;

// Function to set up the boot steps vector
void setup_boot_steps();

// Function to register an application for boot.
// Parameters:
// app_bit - The application bit representing the application to be registered.
// id - The unique identifier (bit) for the application to be registered.
// name - The name of the application.
// Returns:
// true if the application has been successfully registered;
// false otherwise (e.g., invalid bit).
bool register_application(const uint32_t app_bit);
// false if the application is already registered or invalid.
bool register_application(uint8_t id, const char* name);

// Function to get the number of unique boot steps that have been completed.
// Function to mark a boot step as completed.
// Parameters:
// id - The unique identifier for the boot step to be marked as completed.
// Returns:
// The count of completed boot steps as an integer.
uint8_t get_boot_steps_completed();
// true if the boot step has been successfully marked completed;
// false if the id does not exist.
bool complete_boot_step(uint8_t id);

// Function to calculate the boot progress percentage with rounding.
// Returns:
Expand All @@ -43,16 +57,24 @@ namespace nspanel_ha_blueprint {
// true if all registered applications have completed boot, false otherwise.
bool is_boot_complete();

// Function to reset the completed boot steps and registered applications to zero.
// This clears all tracked boot steps and applications.
// Function to reset the completed boot steps to the initial state.
void reset_boot_steps();

// Function to check if a specific boot step has been completed.
// Parameters:
// step - The boot step constant representing the step to check.
// id - The unique identifier for the boot step to check.
// Returns:
// true if the boot step has been completed; false otherwise.
bool is_boot_step_completed(uint32_t step);
bool is_boot_step_completed(uint8_t id);

// Function to get the total number of registered boot steps
uint8_t get_total_registered_boot_steps();

// Function to get the total number of completed boot steps
uint8_t get_completed_boot_steps();

// Function to get a pointer to a specific boot step by its ID
BootStep* get_boot_step(uint8_t id);

} // namespace nspanel_ha_blueprint

Expand Down
19 changes: 17 additions & 2 deletions esphome/nspanel_esphome_core_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ substitutions:
ota_password: ${wifi_password}
project_tag: nspanel_ha_blueprint
##############################
BOOT_STEP_API: '1UL << 30'
BOOT_STEP_API: '30'

api:
id: api_server
Expand Down Expand Up @@ -90,6 +90,21 @@ script:
- delay: 2s
- lambda: api_server->on_shutdown();

- id: !extend boot_package_register
then:
- lambda: boot_package_register_add->execute(${BOOT_STEP_API}, "API");

- id: !extend dump_config
then:
- lambda: |-
static const char *const TAG = "${project_tag}";
// report API status
if (api_server->is_connected()) {
ESP_LOGCONFIG(TAG, "API: Connected");
} else {
ESP_LOGW(TAG, "API: DISCONNECTED");
}
- id: ha_button
mode: parallel
parameters:
Expand Down Expand Up @@ -137,7 +152,7 @@ script:
mode: queued
parameters:
component: string
val: float
val: int
then: # There's nothing here so far

- id: set_var_string
Expand Down
18 changes: 4 additions & 14 deletions esphome/nspanel_esphome_core_base.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ substitutions:
ota_password: ${wifi_password}
project_tag: nspanel_ha_blueprint
##############################
BOOT_STEP_BASE: '1UL << 0'
BOOT_STEP_BASE: '0'

button:
- id: nspanel_factory_reset # Factory Reset button - Used to clean values from flash
Expand Down Expand Up @@ -114,31 +114,21 @@ safe_mode:
- script.execute: safe_mode_loop

script:
- id: !extend boot_progress_dump
- id: !extend boot_package_register
then:
- lambda: |-
boot_progress_dump_item->execute(${BOOT_STEP_BASE}, "Base");
- lambda: boot_package_register_add->execute(${BOOT_STEP_BASE}, "Base");

- id: !extend boot_sequence
then:
- lambda: |-
boot_progress->execute(${BOOT_STEP_BASE}, "Base");
boot_progress->execute(${BOOT_STEP_BASE});
- id: dump_config
mode: restart
then:
- delay: 90s
- lambda: |-
static const char *const TAG = "${project_tag}";
// report API status
if (api_server->is_connected()) {
ESP_LOGCONFIG(TAG, "API: Connected");
} else {
ESP_LOGW(TAG, "API: DISCONNECTED");
}
// if (!(wifi_component->is_connected()) or !api_server->is_connected()) reset_boot_steps();
// Report ESPHome
ESP_LOGCONFIG(TAG, "ESPHome:");
ESP_LOGCONFIG(TAG, " Version: ${version}");
Expand Down
Loading

0 comments on commit 334163e

Please sign in to comment.