feat(sacn): Uebernahme-Schicht mit Timeout, Prioritaet und Sequenz
This commit is contained in:
@@ -74,6 +74,7 @@ set(NL_FW_SOURCES
|
|||||||
src/board/nl_board.c
|
src/board/nl_board.c
|
||||||
src/light/nl_color.c
|
src/light/nl_color.c
|
||||||
src/light/nl_engine.c
|
src/light/nl_engine.c
|
||||||
|
src/light/nl_dmx.c
|
||||||
src/light/nl_control.c
|
src/light/nl_control.c
|
||||||
src/light/nl_boot_state.c
|
src/light/nl_boot_state.c
|
||||||
src/config/nl_settings.c
|
src/config/nl_settings.c
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
#include "light/nl_dmx.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "light/nl_engine.h"
|
||||||
|
|
||||||
|
static const nl_board_t *s_board;
|
||||||
|
static uint8_t s_slots; /* 3 oder 6 */
|
||||||
|
static nl_dmx_cfg_t s_cfg;
|
||||||
|
static bool s_streaming; /* es fliessen Pakete */
|
||||||
|
static bool s_override; /* die Engine haelt unsere Kanaele */
|
||||||
|
static uint8_t s_prio;
|
||||||
|
static uint8_t s_seq;
|
||||||
|
static uint32_t s_last_ms;
|
||||||
|
|
||||||
|
void nl_dmx_init(const nl_board_t *board)
|
||||||
|
{
|
||||||
|
s_board = board;
|
||||||
|
s_slots = nl_board_dmx_slots(board);
|
||||||
|
s_cfg.start_slot = 1;
|
||||||
|
s_cfg.hold = false;
|
||||||
|
s_cfg.release_fade_ms = 0;
|
||||||
|
s_streaming = false;
|
||||||
|
s_override = false;
|
||||||
|
s_prio = 0;
|
||||||
|
s_seq = 0;
|
||||||
|
s_last_ms = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_dmx_configure(const nl_dmx_cfg_t *cfg) { s_cfg = *cfg; }
|
||||||
|
|
||||||
|
bool nl_dmx_active(void) { return s_override; }
|
||||||
|
|
||||||
|
void nl_dmx_release(void)
|
||||||
|
{
|
||||||
|
if (s_override)
|
||||||
|
nl_engine_override_clear(s_cfg.release_fade_ms);
|
||||||
|
s_override = false;
|
||||||
|
s_streaming = false;
|
||||||
|
s_prio = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Der Strom ist zu Ende - entweder durch das terminated-Bit oder durch Timeout. Im
|
||||||
|
* "release"-Modus geben wir den Override frei und blenden zurueck zum Zustand von
|
||||||
|
* nl_ctrl; im "hold"-Modus bleiben die zuletzt gesetzten Kanalwerte einfach stehen. */
|
||||||
|
static void stream_ended(void)
|
||||||
|
{
|
||||||
|
s_streaming = false;
|
||||||
|
s_prio = 0;
|
||||||
|
if (!s_cfg.hold && s_override) {
|
||||||
|
nl_engine_override_clear(s_cfg.release_fade_ms);
|
||||||
|
s_override = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t scale(uint8_t v, uint8_t dim)
|
||||||
|
{
|
||||||
|
return (uint8_t)(((uint16_t)v * (uint16_t)dim + 127u) / 255u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_dmx_feed(const nl_e131_packet_t *p, uint32_t now_ms)
|
||||||
|
{
|
||||||
|
const uint8_t *s;
|
||||||
|
uint8_t ch[NL_CH_COUNT];
|
||||||
|
uint8_t dim;
|
||||||
|
|
||||||
|
if (s_board == NULL)
|
||||||
|
return;
|
||||||
|
/* Reicht das Paket bis zu unserem letzten Slot? Ein halb belegter Satz ergaebe auf den
|
||||||
|
* restlichen Kanaelen willkuerliche Werte - also gar nicht erst anwenden. */
|
||||||
|
if ((uint32_t)s_cfg.start_slot + s_slots - 1u > (uint32_t)p->slot_count)
|
||||||
|
return;
|
||||||
|
if (s_streaming) {
|
||||||
|
if (p->priority < s_prio)
|
||||||
|
return;
|
||||||
|
{
|
||||||
|
int8_t d = (int8_t)(p->sequence - s_seq);
|
||||||
|
if (d <= 0 && d > -NL_DMX_SEQ_WINDOW)
|
||||||
|
return; /* veraltet oder Wiederholung */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s_prio = p->priority;
|
||||||
|
s_seq = p->sequence;
|
||||||
|
s_last_ms = now_ms;
|
||||||
|
|
||||||
|
if (p->terminated) { /* die Quelle sagt selbst, dass sie fertig ist */
|
||||||
|
stream_ended();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_streaming = true;
|
||||||
|
|
||||||
|
s = p->slots + (s_cfg.start_slot - 1u);
|
||||||
|
dim = s[0];
|
||||||
|
memset(ch, 0, sizeof ch);
|
||||||
|
if (s_board->has_rgb) {
|
||||||
|
ch[NL_CH_R] = scale(s[1], dim);
|
||||||
|
ch[NL_CH_G] = scale(s[2], dim);
|
||||||
|
ch[NL_CH_B] = scale(s[3], dim);
|
||||||
|
ch[NL_CH_CW] = scale(s[4], dim);
|
||||||
|
ch[NL_CH_WW] = scale(s[5], dim);
|
||||||
|
} else {
|
||||||
|
ch[NL_CH_CW] = scale(s[1], dim);
|
||||||
|
ch[NL_CH_WW] = scale(s[2], dim);
|
||||||
|
}
|
||||||
|
nl_engine_override(ch);
|
||||||
|
s_override = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_dmx_tick(uint32_t now_ms)
|
||||||
|
{
|
||||||
|
if (!s_streaming)
|
||||||
|
return;
|
||||||
|
if (now_ms - s_last_ms >= NL_DMX_TIMEOUT_MS)
|
||||||
|
stream_ended();
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef NL_DMX_H
|
||||||
|
#define NL_DMX_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "board/nl_board.h"
|
||||||
|
#include "net/nl_e131.h"
|
||||||
|
|
||||||
|
#define NL_DMX_TIMEOUT_MS 2500u /* E1.31, Abschnitt 6.7.1 */
|
||||||
|
#define NL_DMX_SEQ_WINDOW 20 /* E1.31, Abschnitt 6.7.2 */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t start_slot; /* 1..512 */
|
||||||
|
bool hold; /* false = nach dem Timeout zurueck zum Zustand von nl_ctrl */
|
||||||
|
uint16_t release_fade_ms; /* Dauer der Rueckblende (aus settings.fade_in_ms) */
|
||||||
|
} nl_dmx_cfg_t;
|
||||||
|
|
||||||
|
/* Die Engine muss initialisiert sein. Nicht threadsicher: Aufrufer serialisieren. */
|
||||||
|
void nl_dmx_init(const nl_board_t *board);
|
||||||
|
void nl_dmx_configure(const nl_dmx_cfg_t *cfg);
|
||||||
|
/* Ein vom Parser angenommenes Paket. Prueft Sequenz, Prioritaet und Slotbereich und setzt
|
||||||
|
* bei Annahme den Override. */
|
||||||
|
void nl_dmx_feed(const nl_e131_packet_t *p, uint32_t now_ms);
|
||||||
|
/* Aus dem Licht-Task (alle ~10 ms): prueft den Timeout. */
|
||||||
|
void nl_dmx_tick(uint32_t now_ms);
|
||||||
|
/* Ein HA-/REST-Befehl holt die Kontrolle zurueck - in beiden Timeout-Modi. */
|
||||||
|
void nl_dmx_release(void);
|
||||||
|
/* true, solange die Lampe ferngesteuert ist, einschliesslich der Haltephase. */
|
||||||
|
bool nl_dmx_active(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -45,6 +45,8 @@ nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c)
|
|||||||
nl_test(test_names test_names.c ${NL_SRC}/net/nl_names.c)
|
nl_test(test_names test_names.c ${NL_SRC}/net/nl_names.c)
|
||||||
nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c)
|
nl_test(test_http test_http.c ${NL_SRC}/net/nl_http.c)
|
||||||
nl_test(test_e131 test_e131.c ${NL_SRC}/net/nl_e131.c)
|
nl_test(test_e131 test_e131.c ${NL_SRC}/net/nl_e131.c)
|
||||||
|
nl_test(test_dmx test_dmx.c ${NL_SRC}/light/nl_dmx.c ${NL_SRC}/light/nl_engine.c
|
||||||
|
${NL_SRC}/light/nl_color.c ${NL_SRC}/board/nl_board.c ${NL_SRC}/net/nl_e131.c)
|
||||||
nl_test(test_api test_api.c fake_api_ctx.c ${NL_SRC}/net/nl_api.c ${NL_SRC}/board/nl_board.c
|
nl_test(test_api test_api.c fake_api_ctx.c ${NL_SRC}/net/nl_api.c ${NL_SRC}/board/nl_board.c
|
||||||
${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c fake_flash.c)
|
${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/config/nl_settings.c ${NL_SRC}/util/nl_crc.c fake_flash.c)
|
||||||
target_link_libraries(test_api PRIVATE nl_cjson)
|
target_link_libraries(test_api PRIVATE nl_cjson)
|
||||||
|
|||||||
+287
@@ -0,0 +1,287 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "board/nl_board.h"
|
||||||
|
#include "light/nl_dmx.h"
|
||||||
|
#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];
|
||||||
|
|
||||||
|
/* Baut ein Paket aus slot_data und speist es ein - als Funktion, damit kein Aufrufer
|
||||||
|
* die Adresse eines Rueckgabewerts nehmen muss. */
|
||||||
|
static void feed(uint8_t prio, uint8_t seq, bool terminated, uint16_t count, uint32_t now_ms)
|
||||||
|
{
|
||||||
|
nl_e131_packet_t p;
|
||||||
|
p.universe = 1;
|
||||||
|
p.priority = prio;
|
||||||
|
p.sequence = seq;
|
||||||
|
p.terminated = terminated;
|
||||||
|
p.slot_count = count;
|
||||||
|
p.slots = slot_data;
|
||||||
|
nl_dmx_feed(&p, now_ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void start(const char *board_id, uint16_t start_slot, bool hold)
|
||||||
|
{
|
||||||
|
const nl_board_t *b = nl_board_find(board_id);
|
||||||
|
nl_engine_cfg_t cfg;
|
||||||
|
nl_dmx_cfg_t dc;
|
||||||
|
nl_light_state_t off = { false, 100, NL_MODE_WHITE, 0, 0, 0, 3000 };
|
||||||
|
|
||||||
|
cfg.ct_min = b->ct_min;
|
||||||
|
cfg.ct_max = b->ct_max;
|
||||||
|
cfg.output = sink;
|
||||||
|
cfg.ctx = NULL;
|
||||||
|
nl_engine_init(&cfg);
|
||||||
|
nl_engine_set(&off, 0);
|
||||||
|
nl_dmx_init(b);
|
||||||
|
dc.start_slot = start_slot;
|
||||||
|
dc.hold = hold;
|
||||||
|
dc.release_fade_ms = 0;
|
||||||
|
nl_dmx_configure(&dc);
|
||||||
|
out_calls = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
memset(slot_data, 0, sizeof slot_data);
|
||||||
|
memset(last_ch, 0, sizeof last_ch);
|
||||||
|
}
|
||||||
|
void tearDown(void) {}
|
||||||
|
|
||||||
|
static void test_first_packet_takes_over(void)
|
||||||
|
{
|
||||||
|
/* Dimmer voll, R=10 G=20 B=30 CW=40 WW=50 */
|
||||||
|
uint8_t d[6] = { 255, 10, 20, 30, 40, 50 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_TRUE(nl_dmx_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(20, last_ch[NL_CH_G]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(30, last_ch[NL_CH_B]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(40, last_ch[NL_CH_CW]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(50, last_ch[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_master_dimmer_scales(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 128, 200, 200, 200, 200, 200 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_UINT8_WITHIN(1, 100, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_dimmer_zero_is_dark(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 0, 255, 255, 255, 255, 255 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_TRUE(nl_dmx_active()); /* dunkel, aber sehr wohl ferngesteuert */
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_R]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_board_without_rgb_uses_three_slots(void)
|
||||||
|
{
|
||||||
|
uint8_t d[3] = { 255, 77, 88 };
|
||||||
|
start("g95_cw", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 3, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(77, last_ch[NL_CH_CW]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(88, last_ch[NL_CH_WW]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_start_slot_offset(void)
|
||||||
|
{
|
||||||
|
uint8_t d[9] = { 9, 9, 9, 255, 11, 22, 33, 44, 55 };
|
||||||
|
start("gu10_rgb", 4, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 9, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(11, last_ch[NL_CH_R]);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(55, last_ch[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_packet_too_short_for_our_slots_is_dropped(void)
|
||||||
|
{
|
||||||
|
uint8_t d[4] = { 255, 1, 2, 3 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 4, 1000); /* braucht 6, bekommt 4 */
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_dmx_active());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stale_sequence_is_dropped(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 255, 10, 0, 0, 0, 0 };
|
||||||
|
uint8_t older[6] = { 255, 99, 0, 0, 0, 0 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 50, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]);
|
||||||
|
|
||||||
|
memcpy(slot_data, older, sizeof older);
|
||||||
|
feed(100, 40, false, 6, 1010); /* 10 zurueck: im Fenster, verwerfen */
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]);
|
||||||
|
|
||||||
|
feed(100, 20, false, 6, 1020); /* 30 zurueck: ausserhalb, annehmen */
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(99, 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 };
|
||||||
|
uint8_t low[6] = { 255, 200, 0, 0, 0, 0 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, high, sizeof high);
|
||||||
|
feed(150, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
|
||||||
|
memcpy(slot_data, low, sizeof low);
|
||||||
|
feed(100, 2, false, 6, 1010);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(10, last_ch[NL_CH_R]);
|
||||||
|
|
||||||
|
/* Nach dem Timeout ist wieder jede Prioritaet willkommen. */
|
||||||
|
nl_dmx_tick(1000 + NL_DMX_TIMEOUT_MS + 1);
|
||||||
|
feed(100, 3, false, 6, 5000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(200, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_timeout_release_returns_to_engine(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 255, 255, 255, 255, 255, 255 };
|
||||||
|
start("gu10_rgb", 1, false); /* die Lampe steht auf "aus" */
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(255, last_ch[NL_CH_R]);
|
||||||
|
|
||||||
|
nl_dmx_tick(1000 + NL_DMX_TIMEOUT_MS - 1);
|
||||||
|
TEST_ASSERT_TRUE(nl_dmx_active());
|
||||||
|
nl_dmx_tick(1000 + NL_DMX_TIMEOUT_MS);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_dmx_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_R]); /* wieder aus, wie von nl_ctrl gesetzt */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_timeout_hold_keeps_last_values(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 255, 123, 0, 0, 0, 0 };
|
||||||
|
start("gu10_rgb", 1, true);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
|
||||||
|
nl_dmx_tick(1000 + NL_DMX_TIMEOUT_MS + 500);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_TRUE(nl_dmx_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(123, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_hold_keeps_darkness(void)
|
||||||
|
{
|
||||||
|
uint8_t dark[6] = { 0, 0, 0, 0, 0, 0 };
|
||||||
|
nl_light_state_t on = { true, 100, NL_MODE_WHITE, 0, 0, 0, 3000 };
|
||||||
|
start("gu10_rgb", 1, true);
|
||||||
|
nl_engine_set(&on, 0); /* HA haette die Lampe gern an */
|
||||||
|
memcpy(slot_data, dark, sizeof dark);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_dmx_tick(1000 + NL_DMX_TIMEOUT_MS + 1);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
/* Genau der Fall, fuer den "hold" existiert: es bleibt dunkel. */
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_WW]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_terminated_bit_ends_immediately(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 255, 255, 0, 0, 0, 0 };
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
feed(100, 2, true, 6, 1010);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_dmx_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_release_hands_control_back_in_both_modes(void)
|
||||||
|
{
|
||||||
|
uint8_t d[6] = { 255, 255, 0, 0, 0, 0 };
|
||||||
|
int mode;
|
||||||
|
for (mode = 0; mode < 2; mode++) {
|
||||||
|
start("gu10_rgb", 1, mode == 1);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 6, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_TRUE(nl_dmx_active());
|
||||||
|
nl_dmx_release();
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_FALSE(nl_dmx_active());
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_configure_during_stream_takes_effect(void)
|
||||||
|
{
|
||||||
|
uint8_t d[9] = { 255, 1, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
nl_dmx_cfg_t dc;
|
||||||
|
start("gu10_rgb", 1, false);
|
||||||
|
memcpy(slot_data, d, sizeof d);
|
||||||
|
feed(100, 1, false, 9, 1000);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(1, last_ch[NL_CH_R]);
|
||||||
|
|
||||||
|
slot_data[3] = 255; /* Slot 4 = Dimmer, wenn start_slot 4 ist */
|
||||||
|
slot_data[4] = 222; /* Slot 5 = R, wenn start_slot 4 ist */
|
||||||
|
dc.start_slot = 4;
|
||||||
|
dc.hold = false;
|
||||||
|
dc.release_fade_ms = 0;
|
||||||
|
nl_dmx_configure(&dc);
|
||||||
|
feed(100, 2, false, 9, 1010);
|
||||||
|
nl_engine_tick(10);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(222, last_ch[NL_CH_R]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_first_packet_takes_over);
|
||||||
|
RUN_TEST(test_master_dimmer_scales);
|
||||||
|
RUN_TEST(test_dimmer_zero_is_dark);
|
||||||
|
RUN_TEST(test_board_without_rgb_uses_three_slots);
|
||||||
|
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_lower_priority_is_ignored_while_streaming);
|
||||||
|
RUN_TEST(test_timeout_release_returns_to_engine);
|
||||||
|
RUN_TEST(test_timeout_hold_keeps_last_values);
|
||||||
|
RUN_TEST(test_hold_keeps_darkness);
|
||||||
|
RUN_TEST(test_terminated_bit_ends_immediately);
|
||||||
|
RUN_TEST(test_release_hands_control_back_in_both_modes);
|
||||||
|
RUN_TEST(test_configure_during_stream_takes_effect);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user