feat: Parser für die UART-Testkonsole
This commit is contained in:
@@ -0,0 +1,120 @@
|
|||||||
|
#include "sys/nl_console_cmd.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define MAX_TOK 6
|
||||||
|
|
||||||
|
const char nl_console_help[] =
|
||||||
|
"on [ms] | off [ms] | bri <1-100> [ms] | rgb <r> <g> <b> [ms] | ct <K> [ms]\r\n"
|
||||||
|
"pob last|on|off | fade <in> <out> | blink [n] | pulse on|off\r\n"
|
||||||
|
"state | factory | reboot | help\r\n";
|
||||||
|
|
||||||
|
static bool num(const char *s, int32_t lo, int32_t hi, int32_t *out)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
long v = strtol(s, &end, 10);
|
||||||
|
if (*s == '\0' || *end != '\0' || v < lo || v > hi)
|
||||||
|
return false;
|
||||||
|
*out = (int32_t)v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parses tok[first..n-1] into arg[]: `req` required values in [lo,hi], optionally one
|
||||||
|
* trailing transition in [0,60000]. */
|
||||||
|
static bool args(nl_cmd_t *c, char **tok, int n, int req, int32_t lo, int32_t hi, bool opt_ms)
|
||||||
|
{
|
||||||
|
int given = n - 1;
|
||||||
|
if (given < req || given > req + (opt_ms ? 1 : 0))
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < req; i++)
|
||||||
|
if (!num(tok[1 + i], lo, hi, &c->arg[i]))
|
||||||
|
return false;
|
||||||
|
if (given > req && !num(tok[1 + req], 0, 60000, &c->arg[req]))
|
||||||
|
return false;
|
||||||
|
c->argc = (uint8_t)given;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool word(const char *s, const char *const *words, int count, int32_t *out)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
if (strcmp(s, words[i]) == 0) {
|
||||||
|
*out = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_cmd_t nl_console_parse(const char *line)
|
||||||
|
{
|
||||||
|
static const char *const POB[] = {"last", "on", "off"};
|
||||||
|
static const char *const OFFON[] = {"off", "on"};
|
||||||
|
nl_cmd_t c;
|
||||||
|
char buf[64];
|
||||||
|
char *tok[MAX_TOK];
|
||||||
|
char *save = NULL;
|
||||||
|
int n = 0;
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
strncpy(buf, line, sizeof buf - 1);
|
||||||
|
buf[sizeof buf - 1] = '\0';
|
||||||
|
for (char *t = strtok_r(buf, " \t\r\n", &save); t; t = strtok_r(NULL, " \t\r\n", &save)) {
|
||||||
|
if (n == MAX_TOK) {
|
||||||
|
c.type = NL_CMD_ERROR;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
tok[n++] = t;
|
||||||
|
}
|
||||||
|
if (n == 0)
|
||||||
|
return c; /* NL_CMD_NONE */
|
||||||
|
|
||||||
|
if (!strcmp(tok[0], "on")) {
|
||||||
|
c.type = NL_CMD_ON;
|
||||||
|
ok = args(&c, tok, n, 0, 0, 0, true);
|
||||||
|
} else if (!strcmp(tok[0], "off")) {
|
||||||
|
c.type = NL_CMD_OFF;
|
||||||
|
ok = args(&c, tok, n, 0, 0, 0, true);
|
||||||
|
} else if (!strcmp(tok[0], "bri")) {
|
||||||
|
c.type = NL_CMD_BRI;
|
||||||
|
ok = args(&c, tok, n, 1, 1, 100, true);
|
||||||
|
} else if (!strcmp(tok[0], "rgb")) {
|
||||||
|
c.type = NL_CMD_RGB;
|
||||||
|
ok = args(&c, tok, n, 3, 0, 255, true);
|
||||||
|
} else if (!strcmp(tok[0], "ct")) {
|
||||||
|
c.type = NL_CMD_CT;
|
||||||
|
ok = args(&c, tok, n, 1, 1000, 20000, true);
|
||||||
|
} else if (!strcmp(tok[0], "fade")) {
|
||||||
|
c.type = NL_CMD_FADE;
|
||||||
|
ok = args(&c, tok, n, 2, 0, 10000, false);
|
||||||
|
} else if (!strcmp(tok[0], "pob")) {
|
||||||
|
c.type = NL_CMD_POB;
|
||||||
|
ok = n == 2 && word(tok[1], POB, 3, &c.arg[0]);
|
||||||
|
c.argc = 1;
|
||||||
|
} else if (!strcmp(tok[0], "pulse")) {
|
||||||
|
c.type = NL_CMD_PULSE;
|
||||||
|
ok = n == 2 && word(tok[1], OFFON, 2, &c.arg[0]);
|
||||||
|
c.argc = 1;
|
||||||
|
} else if (!strcmp(tok[0], "blink")) {
|
||||||
|
c.type = NL_CMD_BLINK;
|
||||||
|
c.arg[0] = 3;
|
||||||
|
c.argc = 1;
|
||||||
|
ok = n == 1 || (n == 2 && num(tok[1], 1, 10, &c.arg[0]));
|
||||||
|
} else {
|
||||||
|
static const struct { const char *name; nl_cmd_type_t type; } SIMPLE[] = {
|
||||||
|
{"state", NL_CMD_STATE}, {"factory", NL_CMD_FACTORY}, {"reboot", NL_CMD_REBOOT}, {"help", NL_CMD_HELP}};
|
||||||
|
ok = false;
|
||||||
|
for (size_t i = 0; i < sizeof SIMPLE / sizeof SIMPLE[0]; i++)
|
||||||
|
if (!strcmp(tok[0], SIMPLE[i].name)) {
|
||||||
|
c.type = SIMPLE[i].type;
|
||||||
|
ok = n == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
c.type = NL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef NL_CONSOLE_CMD_H
|
||||||
|
#define NL_CONSOLE_CMD_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NL_CMD_NONE = 0,
|
||||||
|
NL_CMD_ON,
|
||||||
|
NL_CMD_OFF,
|
||||||
|
NL_CMD_BRI,
|
||||||
|
NL_CMD_RGB,
|
||||||
|
NL_CMD_CT,
|
||||||
|
NL_CMD_POB,
|
||||||
|
NL_CMD_FADE,
|
||||||
|
NL_CMD_STATE,
|
||||||
|
NL_CMD_BLINK,
|
||||||
|
NL_CMD_PULSE,
|
||||||
|
NL_CMD_FACTORY,
|
||||||
|
NL_CMD_REBOOT,
|
||||||
|
NL_CMD_HELP,
|
||||||
|
NL_CMD_ERROR
|
||||||
|
} nl_cmd_type_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
nl_cmd_type_t type;
|
||||||
|
int32_t arg[4];
|
||||||
|
uint8_t argc;
|
||||||
|
} nl_cmd_t;
|
||||||
|
|
||||||
|
nl_cmd_t nl_console_parse(const char *line);
|
||||||
|
|
||||||
|
extern const char nl_console_help[];
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -34,3 +34,4 @@ nl_test(test_settings test_settings.c fake_flash.c ${NL_SRC}/config/nl_settings.
|
|||||||
nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_log.c
|
nl_test(test_state_log test_state_log.c fake_flash.c ${NL_SRC}/config/nl_state_log.c
|
||||||
${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c)
|
${NL_SRC}/light/nl_boot_state.c ${NL_SRC}/util/nl_crc.c)
|
||||||
nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c)
|
nl_test(test_reset_counter test_reset_counter.c fake_flash.c ${NL_SRC}/config/nl_reset_counter.c)
|
||||||
|
nl_test(test_console_cmd test_console_cmd.c ${NL_SRC}/sys/nl_console_cmd.c)
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
#include "unity.h"
|
||||||
|
#include "sys/nl_console_cmd.h"
|
||||||
|
|
||||||
|
void setUp(void) {}
|
||||||
|
void tearDown(void) {}
|
||||||
|
|
||||||
|
static void test_empty_and_unknown(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_NONE, nl_console_parse("").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_NONE, nl_console_parse(" \r\n").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("dance").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_on_off(void)
|
||||||
|
{
|
||||||
|
nl_cmd_t c = nl_console_parse("on");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ON, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(0, c.argc);
|
||||||
|
c = nl_console_parse("off 1500\r");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_OFF, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(1, c.argc);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(1500, c.arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("on 60001").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("on abc").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_bri(void)
|
||||||
|
{
|
||||||
|
nl_cmd_t c = nl_console_parse("bri 50 200");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_BRI, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(2, c.argc);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(50, c.arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(200, c.arg[1]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("bri 0").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("bri").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_rgb(void)
|
||||||
|
{
|
||||||
|
nl_cmd_t c = nl_console_parse("rgb 255 0 128 1000");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_RGB, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_UINT8(4, c.argc);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(128, c.arg[2]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("rgb 255 0").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("rgb 256 0 0").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ct(void)
|
||||||
|
{
|
||||||
|
nl_cmd_t c = nl_console_parse("ct 2700");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_CT, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(2700, c.arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("ct 500").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_pob_fade_blink_pulse(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL_INT32(0, nl_console_parse("pob last").arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(1, nl_console_parse("pob on").arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(2, nl_console_parse("pob off").arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("pob maybe").type);
|
||||||
|
|
||||||
|
nl_cmd_t c = nl_console_parse("fade 300 800");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_FADE, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(800, c.arg[1]);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("fade 300").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("fade 300 10001").type);
|
||||||
|
|
||||||
|
c = nl_console_parse("blink");
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_BLINK, c.type);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(3, c.arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(5, nl_console_parse("blink 5").arg[0]);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL_INT32(1, nl_console_parse("pulse on").arg[0]);
|
||||||
|
TEST_ASSERT_EQUAL_INT32(0, nl_console_parse("pulse off").arg[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_simple_commands(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_STATE, nl_console_parse("state").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_FACTORY, nl_console_parse("factory").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_REBOOT, nl_console_parse("reboot").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_HELP, nl_console_parse("help").type);
|
||||||
|
TEST_ASSERT_EQUAL(NL_CMD_ERROR, nl_console_parse("state now").type);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_empty_and_unknown);
|
||||||
|
RUN_TEST(test_on_off);
|
||||||
|
RUN_TEST(test_bri);
|
||||||
|
RUN_TEST(test_rgb);
|
||||||
|
RUN_TEST(test_ct);
|
||||||
|
RUN_TEST(test_pob_fade_blink_pulse);
|
||||||
|
RUN_TEST(test_simple_commands);
|
||||||
|
return UNITY_END();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user