Restore portable bridge path and delay controls
This commit is contained in:
1 parent
c19f248d97
commit
7c6ebe352a
4 files changed
+97
-42
No files matched your search
@@ -32,8 +32,8 @@ administrator.
|
||||
| Standard non-MQTT repeater or room server | Keeps the normal role CLI. Size-constrained ESP32 artifacts can omit WebConfig and browser WiFi OTA, so their WebConfig/WiFi commands are unavailable. |
|
||||
| Standard logging | Logging does not remove commands by itself. It has the same CLI as the selected role/profile and adds the compiled logging behavior. |
|
||||
| LoRa-OTA (`-ota-`) | LoRa OTA adds the `ota ...` commands; it does not otherwise reduce the role CLI. A portable OTA artifact can still have the portable restrictions described below. |
|
||||
| Portable MQTT observer | Keeps MQTT/WiFi commissioning, bridge control, radio essentials, update commands, basic identity/status commands, `neighbors`, and `discover.neighbors`. The large repeater administration tree is omitted to fit the legacy ESP32 application slot. |
|
||||
| Portable ESP-NOW bridge | Keeps the repeater's role-specific handlers and a reduced common configuration surface containing radio and bridge essentials. |
|
||||
| Portable MQTT observer | Keeps MQTT/WiFi commissioning, bridge control, radio essentials, update commands, basic identity/status commands, `neighbors`, `discover.neighbors`, `outpath`, and `altpath`. The large repeater administration tree is omitted to fit the legacy ESP32 application slot. |
|
||||
| Portable ESP-NOW bridge | Keeps the repeater's role-specific handlers and a reduced common configuration surface containing radio and bridge essentials, including `rxdelay`, `txdelay`, `outpath`, and `altpath`. |
|
||||
| FULL ESP32 | Uses the matching MQTT target with logging off, removes size-based CLI cuts, and restores the complete command surface supported by that role and hardware. |
|
||||
| FULL ESP32 logging | Uses the matching non-MQTT target with debug and packet logging enabled and the complete command surface supported by that role and hardware. |
|
||||
| `no_external_sensors` | Removes optional external-sensor drivers and their settings; it does not remove core repeater discovery or routing commands. |
|
||||
@@ -48,12 +48,13 @@ The portable MQTT observer keeps these command groups:
|
||||
- lifecycle and identity: `reboot`, `poweroff`, `shutdown`, `ver`, `board`,
|
||||
`password`, and `erase` on the local console;
|
||||
- radio operation: `advert`, `advert.zerohop`, `clock`, `clock sync`, `time`,
|
||||
`memory`, `neighbors`, and `discover.neighbors`;
|
||||
`memory`, `neighbors`, `discover.neighbors`, and the remote-client routing
|
||||
controls `outpath` and `altpath`;
|
||||
- browser/update control when compiled: `start ota`, `stop ota`, `ota check`,
|
||||
and `ota update`;
|
||||
- radio essentials through `get`/`set`: radio parameters, TX power, CAD,
|
||||
interference threshold, AGC reset interval, RX gain, repeat state, and
|
||||
applicable FEM controls;
|
||||
interference threshold, AGC reset interval, RX gain, `rxdelay`, `txdelay`,
|
||||
repeat state, and applicable FEM controls;
|
||||
- MQTT, WiFi, NTP, bridge, and alert commands implemented by the observer
|
||||
feature set;
|
||||
- region and onboard-GPS commands that fit and are compiled into the selected
|
||||
|
||||
@@ -7898,6 +7898,54 @@ static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, si
|
||||
out[pos] = 0;
|
||||
}
|
||||
|
||||
static bool isClientPathCommand(const char* command) {
|
||||
return strcmp(command, "get outpath") == 0
|
||||
|| strcmp(command, "set outpath") == 0
|
||||
|| strncmp(command, "set outpath ", 12) == 0
|
||||
|| strcmp(command, "get altpath") == 0
|
||||
|| strcmp(command, "set altpath") == 0
|
||||
|| strncmp(command, "set altpath ", 12) == 0;
|
||||
}
|
||||
|
||||
bool MyMesh::handleClientPathCommand(ClientInfo* sender, char* command, char* reply) {
|
||||
if (!isClientPathCommand(command)) return false;
|
||||
|
||||
bool is_get = strncmp(command, "get ", 4) == 0;
|
||||
bool is_alt = strncmp(command + 4, "altpath", 7) == 0;
|
||||
if (sender == NULL) {
|
||||
strcpy(reply, "Err - command needs remote client context");
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t* stored_path = is_alt ? sender->alt_path : sender->out_path;
|
||||
uint8_t* stored_path_len = is_alt ? &sender->alt_path_len : &sender->out_path_len;
|
||||
if (is_get) {
|
||||
formatPathReply(stored_path, *stored_path_len, reply, 160);
|
||||
return true;
|
||||
}
|
||||
|
||||
char* spec = command + 11; // length of "set outpath" or "set altpath"
|
||||
if (*spec == ' ') spec++;
|
||||
|
||||
uint8_t path[MAX_PATH_SIZE];
|
||||
uint8_t path_len = OUT_PATH_UNKNOWN;
|
||||
const char* err = NULL;
|
||||
if (!parsePathCommand(spec, path, path_len, err)) {
|
||||
strcpy(reply, err ? err : "Err - invalid path");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) {
|
||||
memset(stored_path, 0, MAX_PATH_SIZE);
|
||||
*stored_path_len = path_len;
|
||||
} else {
|
||||
*stored_path_len = mesh::Packet::copyPath(stored_path, path, path_len);
|
||||
}
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
formatPathReply(stored_path, *stored_path_len, reply, 160);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool commandFamilyMatches(const char* command, const char* family) {
|
||||
size_t len = strlen(family);
|
||||
if (strncmp(command, family, len) != 0) return false;
|
||||
@@ -8048,6 +8096,22 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
|
||||
return;
|
||||
}
|
||||
|
||||
// Reply-path overrides are core remote-client routing controls. Keep them
|
||||
// available before the portable observer hands the remaining commands to
|
||||
// its reduced CommonCLI parser.
|
||||
if (isClientPathCommand(command)) {
|
||||
if (sender && !sender->isAdmin()) {
|
||||
bool allowed = (sender->isRegionMgr() || sender->isFilterMgr())
|
||||
&& isCommonManagerReadOnlyAllowed(command);
|
||||
if (!allowed) {
|
||||
strcpy(reply, "Err - not permitted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
handleClientPathCommand(sender, command, reply);
|
||||
return;
|
||||
}
|
||||
|
||||
// The portable observer exposes its MQTT/WiFi/update controls through
|
||||
// CommonCLI. Omit the repeater's large remote-administration command tree;
|
||||
// its mesh behavior remains fixed by the selected build profile.
|
||||
@@ -8274,43 +8338,8 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
|
||||
Serial.printf("\n");
|
||||
}
|
||||
reply[0] = 0;
|
||||
} else if (strcmp(command, "get outpath") == 0
|
||||
|| strcmp(command, "set outpath") == 0
|
||||
|| strncmp(command, "set outpath ", 12) == 0
|
||||
|| strcmp(command, "get altpath") == 0
|
||||
|| strcmp(command, "set altpath") == 0
|
||||
|| strncmp(command, "set altpath ", 12) == 0) {
|
||||
bool is_get = strncmp(command, "get ", 4) == 0;
|
||||
bool is_alt = strncmp(command + 4, "altpath", 7) == 0;
|
||||
if (sender == NULL) {
|
||||
strcpy(reply, "Err - command needs remote client context");
|
||||
} else {
|
||||
uint8_t* stored_path = is_alt ? sender->alt_path : sender->out_path;
|
||||
uint8_t* stored_path_len = is_alt ? &sender->alt_path_len : &sender->out_path_len;
|
||||
if (is_get) {
|
||||
formatPathReply(stored_path, *stored_path_len, reply, 160);
|
||||
return;
|
||||
}
|
||||
|
||||
char* spec = command + 11; // length of "set outpath" or "set altpath"
|
||||
if (*spec == ' ') spec++;
|
||||
|
||||
uint8_t path[MAX_PATH_SIZE];
|
||||
uint8_t path_len = OUT_PATH_UNKNOWN;
|
||||
const char* err = NULL;
|
||||
if (!parsePathCommand(spec, path, path_len, err)) {
|
||||
strcpy(reply, err ? err : "Err - invalid path");
|
||||
} else {
|
||||
if (path_len == OUT_PATH_UNKNOWN || path_len == OUT_PATH_FORCE_FLOOD) {
|
||||
memset(stored_path, 0, MAX_PATH_SIZE);
|
||||
*stored_path_len = path_len;
|
||||
} else {
|
||||
*stored_path_len = mesh::Packet::copyPath(stored_path, path, path_len);
|
||||
}
|
||||
dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
formatPathReply(stored_path, *stored_path_len, reply, 160);
|
||||
}
|
||||
}
|
||||
} else if (handleClientPathCommand(sender, command, reply)) {
|
||||
return;
|
||||
} else if (strncmp(command, "send text.flood ", 16) == 0) {
|
||||
char* text = trimSpaces(command + 16);
|
||||
if (*text == 0) {
|
||||
|
||||
@@ -468,6 +468,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks
|
||||
void clearFloodRetryBridgeStateByKey(const uint8_t* retry_key);
|
||||
void refreshFloodRetryReachability(const mesh::Packet* packet);
|
||||
void formatFloodRetryPath(char* dest, size_t dest_len, const mesh::Packet* packet) const;
|
||||
bool handleClientPathCommand(ClientInfo* sender, char* command, char* reply);
|
||||
bool formatFloodRetryHeard(char* dest, size_t dest_len, const mesh::Packet* packet) const;
|
||||
void putNeighbour(const mesh::Identity& id, uint32_t timestamp, float snr);
|
||||
uint8_t handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood);
|
||||
|
||||
@@ -3167,6 +3167,26 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "Error, invalid frequency");
|
||||
}
|
||||
#ifdef WITH_BRIDGE
|
||||
} else if (memcmp(config, "rxdelay ", 8) == 0) {
|
||||
float delay = 0.0f;
|
||||
if (mesh::cli::parseDecimalStrict(&config[8], delay)
|
||||
&& delay >= 0.0f && delay <= 20.0f) {
|
||||
_prefs->rx_delay_base = delay;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, must be 0-20");
|
||||
}
|
||||
} else if (memcmp(config, "txdelay ", 8) == 0) {
|
||||
float factor = 0.0f;
|
||||
if (mesh::cli::parseDecimalStrict(&config[8], factor)
|
||||
&& factor >= 0.0f && factor <= 2.0f) {
|
||||
_prefs->tx_delay_factor = factor;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error, must be 0-2");
|
||||
}
|
||||
} else if (memcmp(config, "bridge.enabled ", 15) == 0) {
|
||||
const char* value = &config[15];
|
||||
if (strcmp(value, "on") != 0 && strcmp(value, "off") != 0) {
|
||||
@@ -4178,6 +4198,10 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
} else if (strcmp(config, "role") == 0) {
|
||||
sprintf(reply, "> %s", _callbacks->getRole());
|
||||
#ifdef WITH_BRIDGE
|
||||
} else if (strcmp(config, "rxdelay") == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->rx_delay_base));
|
||||
} else if (strcmp(config, "txdelay") == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
|
||||
} else if (strcmp(config, "bridge.enabled") == 0) {
|
||||
sprintf(reply, "> %s", _prefs->bridge_enabled ? "on" : "off");
|
||||
} else if (strcmp(config, "bridge.delay") == 0) {
|
||||
|
||||
Reference in new issue
Block a user