fix(sacn): sequence handling on priority change and start_slot clamp
This commit is contained in:
+18
-2
@@ -27,7 +27,15 @@ void nl_dmx_init(const nl_board_t *board)
|
|||||||
s_last_ms = 0;
|
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; }
|
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 (s_streaming) {
|
||||||
if (p->priority < s_prio)
|
if (p->priority < s_prio)
|
||||||
return;
|
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);
|
int8_t d = (int8_t)(p->sequence - s_seq);
|
||||||
if (d <= 0 && d > -NL_DMX_SEQ_WINDOW)
|
if (d <= 0 && d > -NL_DMX_SEQ_WINDOW)
|
||||||
return; /* veraltet oder Wiederholung */
|
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_prio = p->priority;
|
||||||
s_seq = p->sequence;
|
s_seq = p->sequence;
|
||||||
|
|||||||
+42
-3
@@ -5,13 +5,11 @@
|
|||||||
#include "light/nl_engine.h"
|
#include "light/nl_engine.h"
|
||||||
|
|
||||||
static uint8_t last_ch[NL_CH_COUNT];
|
static uint8_t last_ch[NL_CH_COUNT];
|
||||||
static int out_calls;
|
|
||||||
|
|
||||||
static void sink(const uint8_t ch[NL_CH_COUNT], void *ctx)
|
static void sink(const uint8_t ch[NL_CH_COUNT], void *ctx)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
memcpy(last_ch, ch, sizeof last_ch);
|
memcpy(last_ch, ch, sizeof last_ch);
|
||||||
out_calls++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t slot_data[16];
|
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.hold = hold;
|
||||||
dc.release_fade_ms = 0;
|
dc.release_fade_ms = 0;
|
||||||
nl_dmx_configure(&dc);
|
nl_dmx_configure(&dc);
|
||||||
out_calls = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUp(void)
|
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]);
|
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)
|
static void test_lower_priority_is_ignored_while_streaming(void)
|
||||||
{
|
{
|
||||||
uint8_t high[6] = { 255, 10, 0, 0, 0, 0 };
|
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_start_slot_offset);
|
||||||
RUN_TEST(test_packet_too_short_for_our_slots_is_dropped);
|
RUN_TEST(test_packet_too_short_for_our_slots_is_dropped);
|
||||||
RUN_TEST(test_stale_sequence_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_lower_priority_is_ignored_while_streaming);
|
||||||
RUN_TEST(test_timeout_release_returns_to_engine);
|
RUN_TEST(test_timeout_release_returns_to_engine);
|
||||||
RUN_TEST(test_timeout_hold_keeps_last_values);
|
RUN_TEST(test_timeout_hold_keeps_last_values);
|
||||||
|
|||||||
Reference in New Issue
Block a user