Skip to content
This repository was archived by the owner on Aug 3, 2026. It is now read-only.

Commit bfdc017

Browse files
committed
refactor(esplifecycle): simplify to node-name API and expand example coverage
1 parent 9189b95 commit bfdc017

16 files changed

Lines changed: 556 additions & 294 deletions

File tree

CHANGELOG.md

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,38 @@ All notable changes to this project will be documented in this file.
55
## [Unreleased]
66

77
### Added
8-
- ESPStartup-like compatibility APIs: `start()` and `stop()` aliases.
9-
- `NodeBuilder::parallelSafe(bool)` for fluent startup-style tuning.
10-
- `LifecycleConfig` parallel controls for all phases:
11-
- `enableParallelInit`
12-
- `enableParallelDeinit`
13-
- `enableParallelReinit`
14-
- Additional runtime config fields:
15-
- `waitTicks`
16-
- `workerName`
17-
- `workerStackSizeBytes`
18-
- Direct snapshot APIs:
19-
- `LifecycleSnapshot snapshot() const`
20-
- `JsonDocument snapshotJson() const`
21-
- Scope teardown entrypoint:
22-
- `LifecycleResult deinitializeByScopeMask(uint32_t scopeMask)`
8+
- Name-first runtime APIs:
9+
- `reinitialize(std::initializer_list<const char*>)`
10+
- `reinitialize(const std::vector<const char*>&)`
11+
- `deinitialize(std::initializer_list<const char*>)`
12+
- `deinitialize(const std::vector<const char*>&)`
13+
- Name-list based reload listener API:
14+
- `startReloadListener(ESPEventBus&, uint16_t, std::function<std::vector<const char*>(void*)>)`
15+
- `stopReloadListener()`
16+
- Extended example suite:
17+
- `dependency-closure`
18+
- `deferred-readiness`
19+
- `parallel-waves`
20+
- `failure-policy`
21+
- `reload-burst`
2322

2423
### Changed
25-
- Scoped teardown now includes transitive dependents of selected scope nodes.
26-
- Scoped reinit now includes selected nodes, their transitive dependents, and required transitive dependencies.
27-
- Runtime scheduling now uses deterministic dependency waves for init/deinit/reinit.
28-
- Parallel execution now supported in init/deinit/reinit waves when enabled.
24+
- Public lifecycle targeting is now node-name based.
25+
- Partial deinitialize expands selected nodes with transitive dependents.
26+
- Partial reinitialize expands selected nodes with dependents and required dependencies.
27+
- Reload listener now consumes node-name payloads and coalesces deduplicated names.
28+
29+
### Removed
30+
- `start()` / `stop()` compatibility aliases.
31+
- Scope-mask-based APIs:
32+
- `deinitializeByScopeMask(...)`
33+
- `reinitializeByScopeMask(...)`
34+
- `reinitializeByNodeNames(...)`
35+
- Node scope bit tagging API:
36+
- `NodeBuilder::reloadScope(...)`
2937

3038
### Fixed
31-
- Dependency closure behavior now matches lifecycle correctness requirements for partial reinit.
32-
- Snapshot metadata now includes phase and last operation/error context for websocket/UI consumers.
39+
- Error reporting now uses node-focused resolution failures (`UnknownNode`, `NodeResolutionFailed`).
3340

3441
## [0.1.0] - 2026-03-05
3542
### Added

README.md

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ESPLifecycle
22

3-
ESPLifecycle is the ESPToolKit lifecycle orchestrator for deterministic init, deinit, and scoped reinit with ESPStartup-like fluent registration.
3+
ESPLifecycle is the ESPToolKit lifecycle orchestrator for deterministic init, deinit, and node-targeted reinit.
44

55
## CI / Release / License
66
[![CI](https://github.com/ESPToolKit/esp-lifecycle/actions/workflows/ci.yml/badge.svg)](https://github.com/ESPToolKit/esp-lifecycle/actions/workflows/ci.yml)
@@ -9,12 +9,12 @@ ESPLifecycle is the ESPToolKit lifecycle orchestrator for deterministic init, de
99

1010
## Features
1111
- Section-based orchestration with dependency validation (`after` / `before`).
12-
- Deterministic `initialize()`, `deinitialize()`, `reinitialize*()`.
13-
- Scoped deinit/reinit closure with dependency correctness.
12+
- Deterministic `initialize()`, `deinitialize()`, and partial `reinitialize(...)`.
13+
- Node-name targeted closure with dependency correctness.
1414
- Optional parallel waves for init, deinit, and reinit.
1515
- Deferred sections with readiness gates.
1616
- Snapshot callback + direct `snapshotJson()` helper (ArduinoJson V7).
17-
- Optional scope listener with 25ms coalescing.
17+
- Optional reload listener with 25ms coalescing and node-name payload mapping.
1818

1919
## Installation
2020
- PlatformIO: add `https://github.com/ESPToolKit/esp-lifecycle.git` to `lib_deps`.
@@ -30,7 +30,7 @@ Dependencies:
3030
#include <ESPLifecycle.h>
3131
```
3232

33-
## Quick Start (ESPStartup-like)
33+
## Quick Start
3434
```cpp
3535
#include <ESPLifecycle.h>
3636
#include <ESPWorker.h>
@@ -46,7 +46,6 @@ void setup() {
4646
cfg.enableParallelInit = true;
4747
cfg.enableParallelDeinit = true;
4848
cfg.enableParallelReinit = true;
49-
cfg.onReady = []() { Serial.println("ready"); };
5049

5150
lifecycle.configure(cfg);
5251
lifecycle.init({"core", "network"});
@@ -55,9 +54,11 @@ void setup() {
5554
lifecycle.addTo("core", "storage", []() { return true; }, []() { return true; });
5655
lifecycle.addTo("network", "wifi", []() { return true; }, []() { return true; }).after("logger");
5756

58-
if (!lifecycle.start()) {
59-
Serial.println("lifecycle start failed");
60-
}
57+
(void)lifecycle.build();
58+
(void)lifecycle.initialize();
59+
60+
// Reinitialize logger and everything that depends on it.
61+
(void)lifecycle.reinitialize({"logger"});
6162
}
6263
```
6364

@@ -71,10 +72,10 @@ lifecycle.section("services")
7172
);
7273
```
7374
74-
## Scope Semantics
75-
- `deinitializeByScopeMask(mask)` expands to target nodes + transitive dependents.
76-
- `reinitializeByScopeMask(mask)` expands to target nodes + dependents + required dependencies.
77-
- `reinitializeByNodeNames(...)` uses the same reinit closure rule.
75+
## Node-Name Targeting Semantics
76+
- `deinitialize({"logger"})` expands to selected nodes + transitive dependents.
77+
- `reinitialize({"logger"})` expands to selected nodes + dependents + required dependencies.
78+
- `reinitializeAll()` keeps full-graph behavior.
7879
7980
## Snapshot API
8081
```cpp
@@ -92,13 +93,15 @@ serializeJson(json, Serial);
9293

9394
## Runtime API
9495
- `LifecycleResult build()`
95-
- `bool start()` / `void stop()` compatibility aliases
9696
- `LifecycleResult initialize()`
9797
- `LifecycleResult deinitialize()`
98-
- `LifecycleResult deinitializeByScopeMask(uint32_t scopeMask)`
98+
- `LifecycleResult deinitialize(std::initializer_list<const char*> nodeNames)`
99+
- `LifecycleResult deinitialize(const std::vector<const char*>& nodeNames)`
99100
- `LifecycleResult reinitializeAll()`
100-
- `LifecycleResult reinitializeByScopeMask(uint32_t scopeMask)`
101-
- `LifecycleResult reinitializeByNodeNames(const std::vector<const char*>& nodeNames)`
101+
- `LifecycleResult reinitialize(std::initializer_list<const char*> nodeNames)`
102+
- `LifecycleResult reinitialize(const std::vector<const char*>& nodeNames)`
103+
- `bool startReloadListener(ESPEventBus&, uint16_t, std::function<std::vector<const char*>(void*)>)`
104+
- `void stopReloadListener()`
102105
- `LifecycleSnapshot snapshot() const`
103106
- `JsonDocument snapshotJson() const`
104107

@@ -109,15 +112,14 @@ serializeJson(json, Serial);
109112
- `continueTeardownOnFailure=false` stops at first teardown failure.
110113
- Busy transitions return `LifecycleErrorCode::Busy`.
111114

112-
## Migration from ESPStartup
113-
- `start()` exists as compatibility alias.
114-
- `stop()` maps to `deinitialize()`.
115-
- Each node must provide `init` and `teardown` callbacks.
116-
- Parallel config now exists for all phases.
117-
118115
## Examples
119116
- `examples/basic-startup`
120117
- `examples/scoped-reload`
118+
- `examples/dependency-closure` - demonstrates dependent closure for partial deinit/reinit.
119+
- `examples/deferred-readiness` - deferred section gate using readiness callbacks.
120+
- `examples/parallel-waves` - parallel init/deinit/reinit wave behavior.
121+
- `examples/failure-policy` - rollback and teardown failure policy behavior.
122+
- `examples/reload-burst` - listener burst coalescing and deduplicated node-name reloads.
121123

122124
## License
123125
MIT - see [LICENSE.md](LICENSE.md).

examples/basic-startup/basic-startup.ino

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,15 @@ void setup() {
2626
lifecycle.addTo("core", "storage", []() { return true; }, []() { return true; });
2727
lifecycle.addTo("network", "wifi", []() { return true; }, []() { return true; }).after("storage");
2828

29-
if( !lifecycle.start() ){
30-
Serial.println("start failed");
29+
LifecycleResult buildResult = lifecycle.build();
30+
if( !buildResult.ok ){
31+
Serial.println("build failed");
32+
return;
33+
}
34+
35+
LifecycleResult initResult = lifecycle.initialize();
36+
if( !initResult.ok ){
37+
Serial.println("initialize failed");
3138
return;
3239
}
3340

@@ -36,7 +43,7 @@ void setup() {
3643
Serial.println();
3744

3845
delay(1000);
39-
lifecycle.stop();
46+
(void)lifecycle.deinitialize({"logger"});
4047
}
4148

4249
void loop() {
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#include <Arduino.h>
2+
#include <ESPLifecycle.h>
3+
#include <ESPWorker.h>
4+
5+
ESPWorker worker;
6+
ESPLifecycle lifecycle;
7+
8+
bool initCore() {
9+
Serial.println("init core");
10+
return true;
11+
}
12+
13+
bool initCloud() {
14+
Serial.println("init cloud");
15+
return true;
16+
}
17+
18+
bool deinitCore() {
19+
Serial.println("deinit core");
20+
return true;
21+
}
22+
23+
bool deinitCloud() {
24+
Serial.println("deinit cloud");
25+
return true;
26+
}
27+
28+
bool cloudReady() {
29+
return millis() > 4000;
30+
}
31+
32+
void waitForReady(TickType_t waitTicks) {
33+
(void)waitTicks;
34+
delay(250);
35+
}
36+
37+
void setup() {
38+
Serial.begin(115200);
39+
40+
worker.init(ESPWorker::Config{});
41+
42+
LifecycleConfig config{};
43+
config.worker = &worker;
44+
config.onReady = []() { Serial.println("all sections ready"); };
45+
46+
lifecycle.configure(config);
47+
lifecycle.init({"core", "cloud"});
48+
49+
lifecycle.section("cloud")
50+
.mode(LifecycleSectionMode::Deferred)
51+
.readiness(cloudReady, waitForReady);
52+
53+
lifecycle.addTo("core", "core-init", initCore, deinitCore);
54+
lifecycle.addTo("cloud", "cloud-sync", initCloud, deinitCloud).after("core-init");
55+
56+
(void)lifecycle.build();
57+
(void)lifecycle.initialize();
58+
}
59+
60+
void loop() {
61+
delay(500);
62+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <Arduino.h>
2+
#include <ESPLifecycle.h>
3+
#include <ESPWorker.h>
4+
5+
ESPWorker worker;
6+
ESPLifecycle lifecycle;
7+
8+
static bool loggerReady = false;
9+
static bool wifiReady = false;
10+
static bool apiReady = false;
11+
12+
bool initLogger() {
13+
loggerReady = true;
14+
Serial.println("init logger");
15+
return true;
16+
}
17+
18+
bool initWifi() {
19+
if( !loggerReady ){
20+
return false;
21+
}
22+
wifiReady = true;
23+
Serial.println("init wifi");
24+
return true;
25+
}
26+
27+
bool initApi() {
28+
if( !wifiReady ){
29+
return false;
30+
}
31+
apiReady = true;
32+
Serial.println("init api");
33+
return true;
34+
}
35+
36+
bool deinitLogger() {
37+
loggerReady = false;
38+
Serial.println("deinit logger");
39+
return true;
40+
}
41+
42+
bool deinitWifi() {
43+
wifiReady = false;
44+
Serial.println("deinit wifi");
45+
return true;
46+
}
47+
48+
bool deinitApi() {
49+
apiReady = false;
50+
Serial.println("deinit api");
51+
return true;
52+
}
53+
54+
void setup() {
55+
Serial.begin(115200);
56+
57+
worker.init(ESPWorker::Config{});
58+
59+
LifecycleConfig config{};
60+
config.worker = &worker;
61+
config.enableParallelInit = false;
62+
config.enableParallelDeinit = false;
63+
config.enableParallelReinit = false;
64+
65+
lifecycle.configure(config);
66+
lifecycle.init({"core", "network", "services"});
67+
68+
lifecycle.addTo("core", "logger", initLogger, deinitLogger);
69+
lifecycle.addTo("network", "wifi", initWifi, deinitWifi).after("logger");
70+
lifecycle.addTo("services", "api", initApi, deinitApi).after("wifi");
71+
72+
(void)lifecycle.build();
73+
(void)lifecycle.initialize();
74+
75+
Serial.println("Reinitialize logger -> expects wifi+api closure");
76+
(void)lifecycle.reinitialize({"logger"});
77+
78+
Serial.println("Deinitialize logger -> expects wifi+api teardown");
79+
(void)lifecycle.deinitialize({"logger"});
80+
}
81+
82+
void loop() {
83+
delay(500);
84+
}

0 commit comments

Comments
 (0)