diff --git a/src/light/nl_dmx.c b/src/light/nl_dmx.c index 68c1bac..b5bae2e 100644 --- a/src/light/nl_dmx.c +++ b/src/light/nl_dmx.c @@ -27,7 +27,15 @@ void nl_dmx_init(const nl_board_t *board) s_last_ms = 0; } -void nl_dmx_configure(const nl_dmx_cfg_t *cfg) { s_cfg = *cfg; } +void nl_dmx_configure(const nl_dmx_cfg_t *cfg) +{ + s_cfg = *cfg; + /* Sicherheitsnetz: start_slot=0 wuerde unten vor den Puffer lesen. Die REST-Schicht + * validiert 1..512, aber dieses Modul soll dafuer nicht auf einen Aufrufer angewiesen + * sein, den es nicht sehen kann. */ + if (s_cfg.start_slot < 1u) + s_cfg.start_slot = 1u; +} bool nl_dmx_active(void) { return s_override; } @@ -73,11 +81,19 @@ void nl_dmx_feed(const nl_e131_packet_t *p, uint32_t now_ms) if (s_streaming) { if (p->priority < s_prio) return; - { + if (p->priority == s_prio) { + /* E1.31-2018 Section 6.7.2: verwerfen, wenn die Differenz <= 0 und > -20 ist. + * Das Fenster ist bei -20 exklusiv - eine Sequenznummer genau 20 hinter der + * zuletzt angenommenen gilt bereits wieder als neu und wird angenommen. Nicht + * "korrigieren": ein Reviewer hat genau das schon einmal versucht. */ int8_t d = (int8_t)(p->sequence - s_seq); if (d <= 0 && d > -NL_DMX_SEQ_WINDOW) return; /* veraltet oder Wiederholung */ } + /* p->priority > s_prio: eine neue, hoeherpriore Quelle loest die laufende ab. Sie + * fuehrt ihre eigene Sequenzzaehlung - ein Abgleich gegen s_seq der abgeloesten + * Quelle waere Zufall und koennte die neue Quelle bis zu NL_DMX_SEQ_WINDOW Pakete + * lang verwerfen. Also: wie das erste Paket eines frischen Streams behandeln. */ } s_prio = p->priority; s_seq = p->sequence; diff --git a/test/test_dmx.c b/test/test_dmx.c index 51aed52..7d58375 100644 --- a/test/test_dmx.c +++ b/test/test_dmx.c @@ -5,13 +5,11 @@ #include "light/nl_engine.h" static uint8_t last_ch[NL_CH_COUNT]; -static int out_calls; static void sink(const uint8_t ch[NL_CH_COUNT], void *ctx) { (void)ctx; memcpy(last_ch, ch, sizeof last_ch); - out_calls++; } static uint8_t slot_data[16]; @@ -48,7 +46,6 @@ static void start(const char *board_id, uint16_t start_slot, bool hold) dc.hold = hold; dc.release_fade_ms = 0; nl_dmx_configure(&dc); - out_calls = 0; } void setUp(void) @@ -149,6 +146,46 @@ static void test_stale_sequence_is_dropped(void) TEST_ASSERT_EQUAL_UINT8(99, last_ch[NL_CH_R]); } +static void test_a_packet_exactly_twenty_behind_is_accepted(void) +{ + /* E1.31-2018 6.7.2 verwirft "<= 0 und > -20" - bei d == -20 ist das Fenster bereits + * verlassen, das Paket gilt als neu. Ein Reviewer hat das schon einmal fuer einen + * Off-by-one gehalten; dieser Test legt fest, auf welcher Seite die Grenze liegt. */ + uint8_t d[6] = { 255, 10, 0, 0, 0, 0 }; + uint8_t twenty_behind[6] = { 255, 77, 0, 0, 0, 0 }; + start("gu10_rgb", 1, false); + memcpy(slot_data, d, sizeof d); + feed(100, 20, false, 6, 1000); + nl_engine_tick(10); + TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]); + + memcpy(slot_data, twenty_behind, sizeof twenty_behind); + feed(100, 0, false, 6, 1010); /* d = 0 - 20 = -20: ausserhalb des Fensters, annehmen */ + nl_engine_tick(10); + TEST_ASSERT_EQUAL_UINT8(77, last_ch[NL_CH_R]); +} + +static void test_higher_priority_source_takes_over_despite_stale_looking_sequence(void) +{ + /* Quelle A laeuft mit hoher Sequenznummer. Quelle B mit hoeherer Prioritaet startet mit + * einer Sequenz, die - gegen A's Zaehler geprueft - wie ein alter Wiederholer aussaehe. + * B fuehrt aber ihre eigene Zaehlung und muss trotzdem sofort uebernehmen. */ + uint8_t a[6] = { 255, 10, 0, 0, 0, 0 }; + uint8_t b[6] = { 255, 222, 0, 0, 0, 0 }; + start("gu10_rgb", 1, false); + memcpy(slot_data, a, sizeof a); + feed(100, 50, false, 6, 1000); /* Quelle A, Prioritaet 100, Sequenz 50 */ + nl_engine_tick(10); + TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]); + + memcpy(slot_data, b, sizeof b); + /* Quelle B, Prioritaet 150, Sequenz 45: gegen A's Zaehler (50) waere d = -5, also + * innerhalb des Verwerfungsfensters - trotzdem muss B sofort uebernehmen. */ + feed(150, 45, false, 6, 1010); + nl_engine_tick(10); + TEST_ASSERT_EQUAL_UINT8(222, last_ch[NL_CH_R]); +} + static void test_lower_priority_is_ignored_while_streaming(void) { uint8_t high[6] = { 255, 10, 0, 0, 0, 0 }; @@ -276,6 +313,8 @@ int main(void) RUN_TEST(test_start_slot_offset); RUN_TEST(test_packet_too_short_for_our_slots_is_dropped); RUN_TEST(test_stale_sequence_is_dropped); + RUN_TEST(test_a_packet_exactly_twenty_behind_is_accepted); + RUN_TEST(test_higher_priority_source_takes_over_despite_stale_looking_sequence); RUN_TEST(test_lower_priority_is_ignored_while_streaming); RUN_TEST(test_timeout_release_returns_to_engine); RUN_TEST(test_timeout_hold_keeps_last_values);