c2621f192a
- 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>
76 lines
3.0 KiB
C
76 lines
3.0 KiB
C
#include "aes67_tx.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "aes67_cfg.h"
|
|
#include "aes67_web.h"
|
|
#include "lwip/inet.h"
|
|
|
|
// Defaults follow the Riedel Director 4-wire AES67 output; channels 2 (core default).
|
|
static const char AES67_DEFAULTS[] =
|
|
"{\"name\":\"AES67\",\"enabled\":true,\"discovery\":\"sap\",\"mcast\":\"239.69.1.10\","
|
|
"\"port\":5004,\"ttl\":32,\"dscp\":34,\"channels\":2,\"mono_sum\":false,\"encoding\":\"L24\","
|
|
"\"rate\":48000,\"ptime\":1,\"pt\":96,\"ssrc\":0,\"clk_offset\":0,"
|
|
"\"session_id\":1,\"session_ver\":1}";
|
|
|
|
// RTP multicast range 224.0.2.0 - 239.255.255.255 (keeps clear of 224.0.0.x/224.0.1.x, where PTP lives).
|
|
static bool check_mcast(const cJSON *g, char *err, size_t n)
|
|
{
|
|
const cJSON *v = cJSON_GetObjectItemCaseSensitive(g, "mcast");
|
|
struct in_addr a;
|
|
if (cJSON_IsString(v) && inet_aton(v->valuestring, &a)) {
|
|
uint32_t ip = ntohl(a.s_addr);
|
|
if (ip >= 0xE0000200 && ip <= 0xEFFFFFFF) {
|
|
return true;
|
|
}
|
|
}
|
|
snprintf(err, n, "mcast: must be 224.0.2.0 - 239.255.255.255");
|
|
return false;
|
|
}
|
|
|
|
static bool aes67_validate(const cJSON *g, char *err, size_t n)
|
|
{
|
|
static const char *const discovery[] = { "manual", "sap", NULL };
|
|
static const char *const encodings[] = { "L16", "L24", NULL };
|
|
static const double rates[] = { 48000, 96000 };
|
|
static const double ptimes[] = { 0.125, 0.25, 0.333, 1, 4 };
|
|
|
|
bool ok = cfg_check_str(g, "name", 1, 63, err, n) &&
|
|
cfg_check_enum(g, "discovery", discovery, err, n) &&
|
|
check_mcast(g, err, n) &&
|
|
cfg_check_int(g, "port", 1024, 65535, err, n) &&
|
|
cfg_check_int(g, "ttl", 1, 255, err, n) &&
|
|
cfg_check_int(g, "dscp", 0, 63, err, n) &&
|
|
cfg_check_int(g, "channels", 1, 2, err, n) &&
|
|
cfg_check_enum(g, "encoding", encodings, err, n) &&
|
|
cfg_check_num_in(g, "rate", rates, 2, err, n) &&
|
|
cfg_check_num_in(g, "ptime", ptimes, 5, err, n) &&
|
|
cfg_check_int(g, "pt", 96, 127, err, n) &&
|
|
cfg_check_int(g, "ssrc", 0, 4294967295.0, err, n) &&
|
|
cfg_check_int(g, "clk_offset", 0, 4294967295.0, err, n) &&
|
|
cfg_check_int(g, "session_id", 0, 4294967295.0, err, n) &&
|
|
cfg_check_int(g, "session_ver", 0, 4294967295.0, err, n);
|
|
if (ok && cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(g, "mono_sum")) &&
|
|
cJSON_GetObjectItemCaseSensitive(g, "channels")->valuedouble != 1) {
|
|
snprintf(err, n, "channels: must be 1 with mono_sum");
|
|
ok = false;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// Stub (build step 2): counters stay 0 until the RTP sender exists (step 4).
|
|
static void tx_status(cJSON *st)
|
|
{
|
|
cJSON_AddNumberToObject(st, "tx_packets", 0);
|
|
cJSON_AddNumberToObject(st, "underruns", 0);
|
|
}
|
|
|
|
esp_err_t aes67_tx_init(void)
|
|
{
|
|
esp_err_t err = cfg_register("aes67", AES67_DEFAULTS, aes67_validate, NULL);
|
|
if (err != ESP_OK) {
|
|
return err;
|
|
}
|
|
return status_register(tx_status);
|
|
}
|