feat(sacn): Override-Zweig in der Licht-Engine
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
#include "light/nl_engine.h"
|
#include "light/nl_engine.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "light/nl_color.h"
|
#include "light/nl_color.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -19,6 +21,11 @@ static uint32_t s_blink_t;
|
|||||||
static bool s_pulse;
|
static bool s_pulse;
|
||||||
static uint32_t s_pulse_t;
|
static uint32_t s_pulse_t;
|
||||||
|
|
||||||
|
static bool s_ov; /* Override haelt die Ausgabe */
|
||||||
|
static uint8_t s_ov_ch[NL_CH_COUNT]; /* zuletzt gesetzte Override-Kanaele */
|
||||||
|
static uint32_t s_ov_fade_dur; /* > 0: Rueckblende laeuft */
|
||||||
|
static uint32_t s_ov_fade_el;
|
||||||
|
|
||||||
static nl_visual_t to_visual(const nl_light_state_t *s)
|
static nl_visual_t to_visual(const nl_light_state_t *s)
|
||||||
{
|
{
|
||||||
nl_visual_t v;
|
nl_visual_t v;
|
||||||
@@ -69,6 +76,18 @@ static void emit(void)
|
|||||||
v = s_cur;
|
v = s_cur;
|
||||||
}
|
}
|
||||||
nl_visual_to_channels(&v, s_cfg.ct_min, s_cfg.ct_max, ch);
|
nl_visual_to_channels(&v, s_cfg.ct_min, s_cfg.ct_max, ch);
|
||||||
|
|
||||||
|
/* Blink und Pulse sind Rueckmeldungen des Geraets an den Menschen (Werksreset,
|
||||||
|
* Onboarding) - die muessen auch waehrend einer laufenden Show sichtbar bleiben. */
|
||||||
|
if (!s_blink_left && !s_pulse) {
|
||||||
|
if (s_ov) {
|
||||||
|
memcpy(ch, s_ov_ch, sizeof ch);
|
||||||
|
} else if (s_ov_fade_dur) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < NL_CH_COUNT; i++)
|
||||||
|
ch[i] = (uint8_t)lerp(s_ov_ch[i], ch[i], s_ov_fade_el, s_ov_fade_dur);
|
||||||
|
}
|
||||||
|
}
|
||||||
s_cfg.output(ch, s_cfg.ctx);
|
s_cfg.output(ch, s_cfg.ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,6 +103,8 @@ void nl_engine_init(const nl_engine_cfg_t *cfg)
|
|||||||
s_blink_t = 0;
|
s_blink_t = 0;
|
||||||
s_pulse = false;
|
s_pulse = false;
|
||||||
s_pulse_t = 0;
|
s_pulse_t = 0;
|
||||||
|
s_ov = false;
|
||||||
|
s_ov_fade_dur = s_ov_fade_el = 0;
|
||||||
emit();
|
emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +144,12 @@ void nl_engine_tick(uint32_t elapsed_ms)
|
|||||||
{
|
{
|
||||||
uint32_t left = elapsed_ms;
|
uint32_t left = elapsed_ms;
|
||||||
|
|
||||||
|
if (s_ov_fade_dur) {
|
||||||
|
s_ov_fade_el += elapsed_ms;
|
||||||
|
if (s_ov_fade_el >= s_ov_fade_dur)
|
||||||
|
s_ov_fade_dur = s_ov_fade_el = 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (s_iseg < s_nseg) {
|
while (s_iseg < s_nseg) {
|
||||||
seg_t *s = &s_seg[s_iseg];
|
seg_t *s = &s_seg[s_iseg];
|
||||||
if (s->elapsed + left >= s->dur) {
|
if (s->elapsed + left >= s->dur) {
|
||||||
@@ -180,3 +207,24 @@ void nl_engine_pulse(bool enable)
|
|||||||
s_pulse_t = 0;
|
s_pulse_t = 0;
|
||||||
emit();
|
emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nl_engine_override(const uint8_t ch[NL_CH_COUNT])
|
||||||
|
{
|
||||||
|
memcpy(s_ov_ch, ch, sizeof s_ov_ch);
|
||||||
|
s_ov = true;
|
||||||
|
s_ov_fade_dur = s_ov_fade_el = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_engine_override_clear(uint32_t fade_ms)
|
||||||
|
{
|
||||||
|
if (!s_ov)
|
||||||
|
return;
|
||||||
|
s_ov = false;
|
||||||
|
s_ov_fade_dur = fade_ms;
|
||||||
|
s_ov_fade_el = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nl_engine_override_active(void)
|
||||||
|
{
|
||||||
|
return s_ov;
|
||||||
|
}
|
||||||
|
|||||||
@@ -29,4 +29,13 @@ void nl_engine_current(nl_visual_t *out);
|
|||||||
void nl_engine_blink(uint8_t count);
|
void nl_engine_blink(uint8_t count);
|
||||||
void nl_engine_pulse(bool enable);
|
void nl_engine_pulse(bool enable);
|
||||||
|
|
||||||
|
/* DMX-Override: die Kanalwerte gehen unveraendert an die Ausgabe, ohne Fade und ohne
|
||||||
|
* Farbmischung. Rangfolge in emit(): Blink > Pulse > Override > Fade-Engine. Der
|
||||||
|
* darunterliegende Zustand laeuft unberuehrt weiter. */
|
||||||
|
void nl_engine_override(const uint8_t ch[NL_CH_COUNT]);
|
||||||
|
/* Beendet den Override. fade_ms > 0 blendet im Kanalraum auf das zurueck, was die
|
||||||
|
* Fade-Engine gerade ausgeben wuerde; 0 schaltet sofort um. */
|
||||||
|
void nl_engine_override_clear(uint32_t fade_ms);
|
||||||
|
bool nl_engine_override_active(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -183,6 +183,86 @@ static void test_blink_max_clamped(void)
|
|||||||
TEST_ASSERT_TRUE(nl_engine_idle());
|
TEST_ASSERT_TRUE(nl_engine_idle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_override_takes_over_output(void)
|
||||||
|
{
|
||||||
|
nl_light_state_t on = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
||||||
|
uint8_t ov[NL_CH_COUNT] = { 10, 20, 30, 40, 50 };
|
||||||
|
|
||||||
|
nl_engine_set(&on, 0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
||||||
|
|
||||||
|
nl_engine_override(ov);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_TRUE(nl_engine_override_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(10, last[NL_CH_R]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(50, last[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_override_clear_without_fade_is_immediate(void)
|
||||||
|
{
|
||||||
|
nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
||||||
|
uint8_t ov[NL_CH_COUNT] = { 255, 255, 255, 255, 255 };
|
||||||
|
|
||||||
|
nl_engine_set(&off, 0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
nl_engine_override(ov);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(255, last[NL_CH_WW]);
|
||||||
|
|
||||||
|
nl_engine_override_clear(0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_WW]); /* die Lampe war aus, also sofort dunkel */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_override_clear_fades_back(void)
|
||||||
|
{
|
||||||
|
nl_light_state_t off = st(false, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
||||||
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 200 };
|
||||||
|
|
||||||
|
nl_engine_set(&off, 0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
nl_engine_override(ov);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
|
||||||
|
nl_engine_override_clear(1000);
|
||||||
|
nl_engine_tick(500);
|
||||||
|
/* auf halber Strecke zwischen 200 und 0, mit Spielraum fuer die Rundung */
|
||||||
|
TEST_ASSERT_UINT8_WITHIN(12, 100, last[NL_CH_WW]);
|
||||||
|
nl_engine_tick(500);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last[NL_CH_WW]);
|
||||||
|
TEST_ASSERT_FALSE(nl_engine_override_active());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_blink_wins_over_override(void)
|
||||||
|
{
|
||||||
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 0 };
|
||||||
|
|
||||||
|
nl_engine_override(ov);
|
||||||
|
nl_engine_blink(3);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
/* Blink leuchtet hell weiss, obwohl der Override alles auf 0 haelt */
|
||||||
|
TEST_ASSERT_NOT_EQUAL_UINT8(0, last[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_underlying_state_survives_the_override(void)
|
||||||
|
{
|
||||||
|
nl_light_state_t on = st(true, 100, NL_MODE_WHITE, 0, 0, 0, 3000);
|
||||||
|
uint8_t ov[NL_CH_COUNT] = { 0, 0, 0, 0, 0 };
|
||||||
|
uint8_t before;
|
||||||
|
|
||||||
|
nl_engine_set(&on, 0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
before = last[NL_CH_WW];
|
||||||
|
|
||||||
|
nl_engine_override(ov);
|
||||||
|
nl_engine_tick(2000);
|
||||||
|
nl_engine_override_clear(0);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(before, last[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
@@ -197,5 +277,10 @@ int main(void)
|
|||||||
RUN_TEST(test_blink_overrides_and_restores);
|
RUN_TEST(test_blink_overrides_and_restores);
|
||||||
RUN_TEST(test_pulse);
|
RUN_TEST(test_pulse);
|
||||||
RUN_TEST(test_blink_max_clamped);
|
RUN_TEST(test_blink_max_clamped);
|
||||||
|
RUN_TEST(test_override_takes_over_output);
|
||||||
|
RUN_TEST(test_override_clear_without_fade_is_immediate);
|
||||||
|
RUN_TEST(test_override_clear_fades_back);
|
||||||
|
RUN_TEST(test_blink_wins_over_override);
|
||||||
|
RUN_TEST(test_underlying_state_survives_the_override);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user