feat: Licht nach einem OTA wie bei power_on_behaviour=last wiederherstellen
Spec 8.2 verlangt, dass ein durch ein Update ausgelöster Neustart nicht wie ein Stromausfall wirkt: Das Flag ota_pending wurde bisher nur geschrieben und beim nächsten Boot wieder gelöscht, nl_boot_state() wendet aber weiterhin das normale power_on_behaviour an. Eine eingeschaltete Lampe mit pob=off kam nach einem Update aus zurück, mit pob=on umgekehrt. nl_boot_state() bekommt dafür ein Argument after_ota; ist ein gespeicherter Zustand vorhanden, wird er unverändert übernommen. Ohne gespeicherten Zustand gelten weiterhin die normalen Regeln. Hosttests: vier neue Fälle in test_boot_state.c (pob=off/on über ein OTA, Sanitisierung bleibt aktiv, kein gespeicherter Zustand).
This commit is contained in:
+9
-6
@@ -149,7 +149,7 @@ static void app_task(void *arg)
|
||||
uint32_t base = nl_flash_region_base();
|
||||
nl_engine_cfg_t cfg;
|
||||
nl_light_state_t last, initial, pending;
|
||||
bool have_last, counter_cleared = false;
|
||||
bool have_last, after_ota, counter_cleared = false;
|
||||
bool light_alive = false, net_alive = false, http_alive = false;
|
||||
uint8_t boots;
|
||||
uint32_t boot_ms, light_seen, net_seen, http_seen;
|
||||
@@ -165,10 +165,12 @@ static void app_task(void *arg)
|
||||
nl_state_log_init(base + NL_FL_STATE_0, base + NL_FL_STATE_1);
|
||||
have_last = nl_state_log_load(&last);
|
||||
store_unlock();
|
||||
if (s_settings.ota_pending) {
|
||||
/* We came back from an update attempt (accepted or rejected). Just clear the flag so it
|
||||
* doesn't stick in flash forever; nl_boot_state() below restores the light from `last`/
|
||||
* power_on_behaviour as on any other boot, no special-casing needed here. */
|
||||
after_ota = s_settings.ota_pending != 0;
|
||||
if (after_ota) {
|
||||
/* We came back from an update attempt (accepted or rejected). Clear the flag so it does
|
||||
* not stick in flash forever; nl_boot_state() below uses `after_ota` to restore the light
|
||||
* effectively as power_on_behaviour = last (spec 8.2: a reboot caused by an update is not
|
||||
* a power cut). */
|
||||
s_settings.ota_pending = 0;
|
||||
printf("boot after ota attempt\r\n");
|
||||
store_lock();
|
||||
@@ -183,7 +185,8 @@ static void app_task(void *arg)
|
||||
cfg.ct_max = board->ct_max;
|
||||
cfg.output = led_out;
|
||||
cfg.ctx = NULL;
|
||||
initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board);
|
||||
initial = nl_boot_state((nl_pob_t)s_settings.power_on_behaviour, have_last ? &last : NULL, board,
|
||||
after_ota);
|
||||
nl_app_lock();
|
||||
nl_engine_init(&cfg);
|
||||
nl_ctrl_init(board, s_settings.fade_in_ms, s_settings.fade_out_ms);
|
||||
|
||||
@@ -20,7 +20,8 @@ nl_light_state_t nl_boot_default_state(const nl_board_t *b)
|
||||
return s;
|
||||
}
|
||||
|
||||
nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b)
|
||||
nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b,
|
||||
bool after_ota)
|
||||
{
|
||||
nl_light_state_t s = last ? *last : nl_boot_default_state(b);
|
||||
|
||||
@@ -30,6 +31,12 @@ nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const
|
||||
s.mode = NL_MODE_WHITE;
|
||||
s.ct = clamp_ct(s.ct, b);
|
||||
|
||||
/* Spec 8.2: after an OTA the light is restored effectively as power_on_behaviour = last -
|
||||
* a reboot triggered by an update must not look like a power cut to the user. Without a
|
||||
* stored state there is nothing to restore, so the normal rules apply. */
|
||||
if (after_ota && last)
|
||||
return s;
|
||||
|
||||
if (pob == NL_POB_ON)
|
||||
s.on = true;
|
||||
else if (pob == NL_POB_OFF)
|
||||
|
||||
@@ -9,8 +9,12 @@
|
||||
/* Warm white, 100 %, on. */
|
||||
nl_light_state_t nl_boot_default_state(const nl_board_t *b);
|
||||
|
||||
/* State to apply after power-up. last == NULL when nothing is stored. Stored values are sanitised. */
|
||||
nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b);
|
||||
/* State to apply after power-up. last == NULL when nothing is stored. Stored values are sanitised.
|
||||
* after_ota: this boot follows a firmware update (settings flag ota_pending). Spec 8.2 - such a
|
||||
* reboot is not a power cut, so the light comes back effectively as power_on_behaviour = last,
|
||||
* whatever pob says. */
|
||||
nl_light_state_t nl_boot_state(nl_pob_t pob, const nl_light_state_t *last, const nl_board_t *b,
|
||||
bool after_ota);
|
||||
|
||||
bool nl_light_state_equal(const nl_light_state_t *a, const nl_light_state_t *b);
|
||||
|
||||
|
||||
+48
-5
@@ -23,14 +23,14 @@ static void test_default_state(void)
|
||||
static void test_last_restores_everything(void)
|
||||
{
|
||||
nl_light_state_t last = red_off();
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("evk"));
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("evk"), false);
|
||||
TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s));
|
||||
}
|
||||
|
||||
static void test_last_without_saved_state_uses_default(void)
|
||||
{
|
||||
const nl_board_t *b = nl_board_find("evk");
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, NULL, b);
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, NULL, b, false);
|
||||
nl_light_state_t d = nl_boot_default_state(b);
|
||||
TEST_ASSERT_TRUE(nl_light_state_equal(&d, &s));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ static void test_last_without_saved_state_uses_default(void)
|
||||
static void test_on_forces_on_with_last_values(void)
|
||||
{
|
||||
nl_light_state_t last = red_off();
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk"));
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk"), false);
|
||||
TEST_ASSERT_TRUE(s.on);
|
||||
TEST_ASSERT_EQUAL_UINT8(40, s.brightness);
|
||||
TEST_ASSERT_EQUAL(NL_MODE_COLOR, s.mode);
|
||||
@@ -48,7 +48,7 @@ static void test_off_forces_off(void)
|
||||
{
|
||||
nl_light_state_t last = red_off();
|
||||
last.on = true;
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk"));
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk"), false);
|
||||
TEST_ASSERT_FALSE(s.on);
|
||||
TEST_ASSERT_EQUAL_UINT8(40, s.brightness);
|
||||
}
|
||||
@@ -56,12 +56,51 @@ static void test_off_forces_off(void)
|
||||
static void test_sanitizes_stored_values(void)
|
||||
{
|
||||
nl_light_state_t last = {true, 0, NL_MODE_COLOR, 1, 2, 3, 9000};
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw"));
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_LAST, &last, nl_board_find("e27_cw"), false);
|
||||
TEST_ASSERT_EQUAL_UINT8(100, s.brightness); /* invalid 0 -> 100 */
|
||||
TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode); /* no RGB on this board */
|
||||
TEST_ASSERT_EQUAL_UINT16(5000, s.ct); /* clamped */
|
||||
}
|
||||
|
||||
/* Spec 8.2: a reboot caused by an OTA is not a power cut - the light comes back effectively as
|
||||
* power_on_behaviour = last, whatever pob says. */
|
||||
static void test_after_ota_ignores_pob_off(void)
|
||||
{
|
||||
nl_light_state_t last = red_off();
|
||||
nl_light_state_t s;
|
||||
last.on = true;
|
||||
s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("evk"), true);
|
||||
TEST_ASSERT_TRUE(s.on);
|
||||
TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s));
|
||||
}
|
||||
|
||||
static void test_after_ota_ignores_pob_on(void)
|
||||
{
|
||||
nl_light_state_t last = red_off(); /* stored state: off */
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_ON, &last, nl_board_find("evk"), true);
|
||||
TEST_ASSERT_FALSE(s.on);
|
||||
TEST_ASSERT_TRUE(nl_light_state_equal(&last, &s));
|
||||
}
|
||||
|
||||
static void test_after_ota_still_sanitizes(void)
|
||||
{
|
||||
nl_light_state_t last = {true, 200, NL_MODE_COLOR, 1, 2, 3, 9000};
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_OFF, &last, nl_board_find("e27_cw"), true);
|
||||
TEST_ASSERT_TRUE(s.on);
|
||||
TEST_ASSERT_EQUAL_UINT8(100, s.brightness);
|
||||
TEST_ASSERT_EQUAL(NL_MODE_WHITE, s.mode);
|
||||
TEST_ASSERT_EQUAL_UINT16(5000, s.ct);
|
||||
}
|
||||
|
||||
/* Nothing stored (first boot after a migration): there is no "last" to restore, so the normal
|
||||
* power_on_behaviour rules apply even with the flag set. */
|
||||
static void test_after_ota_without_saved_state_uses_pob(void)
|
||||
{
|
||||
const nl_board_t *b = nl_board_find("evk");
|
||||
nl_light_state_t s = nl_boot_state(NL_POB_OFF, NULL, b, true);
|
||||
TEST_ASSERT_FALSE(s.on);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
@@ -71,5 +110,9 @@ int main(void)
|
||||
RUN_TEST(test_on_forces_on_with_last_values);
|
||||
RUN_TEST(test_off_forces_off);
|
||||
RUN_TEST(test_sanitizes_stored_values);
|
||||
RUN_TEST(test_after_ota_ignores_pob_off);
|
||||
RUN_TEST(test_after_ota_ignores_pob_on);
|
||||
RUN_TEST(test_after_ota_still_sanitizes);
|
||||
RUN_TEST(test_after_ota_without_saved_state_uses_pob);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user