Step 2.2: config registry and NVS store, GET/POST /api/config
- aes67_web/cfg.c: cfg_register(group, defaults, validate, apply), cfg_override_defaults() for project defaults, cfg_get(). Stored values are loaded on register and merged over defaults (known keys, matching types). NVS keeps only keys that differ from defaults, one JSON string per group, so new firmware defaults still reach untouched settings. - POST /api/config: type check against defaults, per-group validation, nothing stored unless every group passes; 400 "group.key: reason". Unknown keys/groups are ignored. Returns the new config. - Core groups registered by their components with the UI's ranges: ptp (aes67_ptp), aes67 (aes67_tx, incl. multicast range and mono_sum => 1 channel), net/inet (aes67_net), log (aes67_syslog). - Project: PROJECT.def overrides (hostname p4-aes67, name P4 AES67) and the "source" group in main/project_cfg.c. - Config is stored only; nothing is applied live yet. - Verified on board: defaults, valid save, 8 rejected values (nothing stored), persistence across reboot, restore to defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
idf_component_register(SRCS "aes67_net.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_eth esp_netif esp_event
|
||||
PRIV_REQUIRES aes67_web)
|
||||
PRIV_REQUIRES aes67_web lwip)
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include "aes67_net.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aes67_cfg.h"
|
||||
#include "aes67_web.h"
|
||||
#include "esp_eth.h"
|
||||
#include "esp_event.h"
|
||||
@@ -10,6 +13,14 @@
|
||||
|
||||
static const char *TAG = "net";
|
||||
|
||||
// AES67 / untagged interface.
|
||||
static const char NET_DEFAULTS[] =
|
||||
"{\"hostname\":\"aes67\",\"vlan\":false,\"web_on\":\"both\",\"dhcp\":true,"
|
||||
"\"ip\":\"\",\"mask\":\"\",\"gw\":\"\",\"dns\":\"\"}";
|
||||
// Internet / tagged interface, only used with net.vlan.
|
||||
static const char INET_DEFAULTS[] =
|
||||
"{\"vlan_id\":10,\"pcp\":0,\"dhcp\":true,\"ip\":\"\",\"mask\":\"\",\"gw\":\"\",\"dns\":\"\"}";
|
||||
|
||||
static esp_netif_t *s_netif;
|
||||
static char s_link[24] = "down";
|
||||
|
||||
@@ -48,6 +59,52 @@ static void ip_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
IP2STR(&ev->ip_info.ip), IP2STR(&ev->ip_info.netmask), IP2STR(&ev->ip_info.gw));
|
||||
}
|
||||
|
||||
// Static addressing needs ip + mask; gw/dns are optional (none on the AES67 side with VLAN split).
|
||||
static bool check_addr(const cJSON *g, char *err, size_t n)
|
||||
{
|
||||
bool dhcp = cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(g, "dhcp"));
|
||||
return cfg_check_ipv4(g, "ip", dhcp, err, n) &&
|
||||
cfg_check_ipv4(g, "mask", dhcp, err, n) &&
|
||||
cfg_check_ipv4(g, "gw", true, err, n) &&
|
||||
cfg_check_ipv4(g, "dns", true, err, n);
|
||||
}
|
||||
|
||||
// RFC 1123 label: letters, digits, hyphen; not starting or ending with a hyphen.
|
||||
static bool check_hostname(const cJSON *g, char *err, size_t n)
|
||||
{
|
||||
if (!cfg_check_str(g, "hostname", 1, 63, err, n)) {
|
||||
return false;
|
||||
}
|
||||
const char *h = cJSON_GetObjectItemCaseSensitive(g, "hostname")->valuestring;
|
||||
size_t len = strlen(h);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (!isalnum((unsigned char)h[i]) && h[i] != '-') {
|
||||
snprintf(err, n, "hostname: only letters, digits and '-'");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (h[0] == '-' || h[len - 1] == '-') {
|
||||
snprintf(err, n, "hostname: must not start or end with '-'");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool net_validate(const cJSON *g, char *err, size_t n)
|
||||
{
|
||||
static const char *const web_on[] = { "both", "inet", "aes67", NULL };
|
||||
return check_hostname(g, err, n) &&
|
||||
cfg_check_enum(g, "web_on", web_on, err, n) &&
|
||||
check_addr(g, err, n);
|
||||
}
|
||||
|
||||
static bool inet_validate(const cJSON *g, char *err, size_t n)
|
||||
{
|
||||
return cfg_check_int(g, "vlan_id", 1, 4094, err, n) &&
|
||||
cfg_check_int(g, "pcp", 0, 7, err, n) &&
|
||||
check_addr(g, err, n);
|
||||
}
|
||||
|
||||
static void net_status(cJSON *st)
|
||||
{
|
||||
char ip[16] = "", mac_s[18] = "";
|
||||
@@ -69,6 +126,10 @@ static void net_status(cJSON *st)
|
||||
|
||||
esp_err_t aes67_net_init(esp_eth_handle_t eth)
|
||||
{
|
||||
// Config is stored only for now; applying it (hostname, static IP, VLAN) comes later.
|
||||
ESP_ERROR_CHECK(cfg_register("net", NET_DEFAULTS, net_validate, NULL));
|
||||
ESP_ERROR_CHECK(cfg_register("inet", INET_DEFAULTS, inet_validate, NULL));
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
esp_err_t err = esp_event_loop_create_default();
|
||||
if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
|
||||
|
||||
Reference in New Issue
Block a user