sync with vanilla dev

This commit is contained in:
liquidraver
2026-05-24 21:07:38 +02:00
parent 051adef93e
commit 99279fd9fa
4 changed files with 66 additions and 2 deletions
+3
View File
@@ -72,6 +72,7 @@ Regions control which flood packets the repeater forwards. The region tree is hi
| `region` | Export the current region map (indented text tree) |
| `region load` | Enter interactive region load mode. Paste indented region lines; send a blank line to commit |
| `region save` | Save the current region map to persistent storage |
| `region def <token> [...]` | Cursor-walk bulk region builder — define a hierarchy in one line (see below) |
| `region put <name> [<parent>]` | Create a region; default parent is the wildcard root. Flood is **allowed** by default (use `region denyf` to deny) |
| `region remove <name>` | Remove a region (must have no children) |
| `region get <name>` | Show a region's parent and flood-allow flag |
@@ -84,6 +85,8 @@ Regions control which flood packets the repeater forwards. The region tree is hi
**Region load format:** one region per line, indented with spaces to indicate depth. Append `F` after the name to mark flood-allowed (otherwise flood is denied by default).
**`region def` format:** space-separated tokens; a cursor starts at `*`. Each token is `name` (create child of cursor, advance cursor to it) or `name|jump` / `name,jump` (create child of cursor, then move cursor to the existing region `jump`). Does **not** auto-save — follow with `region save`. Reply is the updated region tree. Example — branched tree: `region def west pnw or pdx|pnw wa sw-wa`. Example — flat list: `region def west|* pnw|* or|* pdx|*`.
---
## Statistics & Logging
+1 -1
View File
@@ -152,7 +152,7 @@ struct FreqRange {
static const FreqRange repeat_freq_ranges[] = {
{ 433000, 433000 }, /* 433 MHz (ISM band) */
{ 869000, 869000 }, /* 869 MHz (EU ISM band) */
{ 869495, 869495 }, /* 869.495 MHz (EU 869.4-869.65 band, 10% DC, 500mW ERP) */
{ 918000, 918000 }, /* 918 MHz (US ISM band) */
};
+61
View File
@@ -1177,6 +1177,48 @@ void RepeaterMesh::resetDutyCycleTimeoutRestarts() {
getRadioDriver(_radio).resetDutyCycleTimeoutRestarts();
}
/* ---------- region def helpers ---------- */
static char* skipSpaces(char* s) { while (*s == ' ') s++; return s; }
static void rtrimSpaces(char* s) { char* e = s + strlen(s); while (e > s && e[-1] == ' ') *--e = '\0'; }
static char* takeToken(char** cursor) {
char* p = skipSpaces(*cursor);
if (*p == '\0') { *cursor = p; return nullptr; }
char* tok = p;
while (*p && *p != ' ') p++;
if (*p) *p++ = '\0';
*cursor = p;
return tok;
}
static char* splitNameJump(char* tok) {
for (char* q = tok; *q; q++) {
if (*q == '|' || *q == ',') {
*q = '\0';
char* jump = skipSpaces(q + 1);
rtrimSpaces(jump);
return jump;
}
}
return nullptr;
}
static bool processRegionDefSegment(RegionMap* map, char* tok, RegionEntry** cursor, char* reply) {
char* jump = splitNameJump(tok);
char* name = skipSpaces(tok);
if (*name == '\0') { snprintf(reply, 160, "Err - empty name"); return false; }
if (jump && *jump == '\0') { snprintf(reply, 160, "Err - empty jump"); return false; }
RegionEntry* r = map->putRegion(name, (*cursor)->id);
if (r == NULL) { snprintf(reply, 160, "Err - put failed: %s", name); return false; }
r->flags = 0;
if (jump) {
RegionEntry* j = map->findByNamePrefix(jump);
if (j == NULL) { snprintf(reply, 160, "Err - unknown jump: %s", jump); return false; }
*cursor = j;
} else {
*cursor = r;
}
return true;
}
/* ---------------------------------------- */
void RepeaterMesh::handleCommand(uint32_t sender_timestamp, char* command, char* reply) {
if (region_load_active) {
if (StrHelper::isBlank(command)) {
@@ -1278,6 +1320,23 @@ void RepeaterMesh::handleCommand(uint32_t sender_timestamp, char* command, char*
reply[0] = 0;
} else if (memcmp(command, "region", 6) == 0) {
reply[0] = 0;
// `region def`: cursor-walk bulk region builder — must run before parseTextParts
// mutates and truncates the buffer to 4 segments.
char* cmd = skipSpaces(command);
if (strncmp(cmd, "region def", 10) == 0 && (cmd[10] == ' ' || cmd[10] == '\0')) {
char* payload = skipSpaces(cmd + 10);
rtrimSpaces(payload);
if (*payload == '\0') { snprintf(reply, 160, "Err - empty def"); goto region_done; }
RegionEntry* cursor = &region_map.getWildcard();
for (char* tok; (tok = takeToken(&payload)) != nullptr; ) {
if (!processRegionDefSegment(&region_map, tok, &cursor, reply)) goto region_done;
}
region_map.exportTo(reply, 160);
goto region_done;
}
{
const char* parts[4];
int n = mesh::Utils::parseTextParts(command, parts, 4, ' ');
@@ -1400,6 +1459,8 @@ void RepeaterMesh::handleCommand(uint32_t sender_timestamp, char* command, char*
} else {
strcpy(reply, "Err - ??");
}
} // end parseTextParts scope
region_done:;
} else if (memcmp(command, "discover.neighbors", 18) == 0) {
const char* sub = command + 18;
while (*sub == ' ') sub++;
+1 -1
View File
@@ -110,7 +110,7 @@ static inline void initNodePrefs(NodePrefs* prefs) {
#endif
prefs->disable_fwd = 0;
prefs->advert_interval = 0; // 0 = periodic local advert off; else minutes = value * 2
prefs->flood_advert_interval = 25; // hours
prefs->flood_advert_interval = 47; // hours
prefs->rx_delay_base = 0.0f;
prefs->tx_delay_factor = 0.5f;
prefs->direct_tx_delay_factor = 0.3f;