Use shorter CLI validation replies on the constrained Mini

This commit is contained in:
mikecarper committed 2026-10-28 04:15:04 -07:00
1 parent c778abceab
commit cea654cb47
15 files changed
+901 -163

No files matched your search

+65 -64
View File
@@ -1,3 +1,4 @@
#include <helpers/CLIReplyText.h>
#include "MyMesh.h"
#include <helpers/ui/DisplayNightLocation.h>
@@ -234,7 +235,7 @@ static const char FLOOD_PACKET_FILTER_USAGE[] =
#if MESH_ENABLE_FLOOD_RULE_ENGINE
"Err - use: set flood.rule[.n] type=<type> [hops=<range>] [...]";
#else
"Err - use: set flood.filter[.n] <type> [hops] [path=blacklist] [scope=<name>] [require=region] [tx=slow] [suspend=tempradio]";
MESH_CLI_REPLY_TEXT("Err - use: set flood.filter[.n] <type> [hops] [path=blacklist] [scope=<name>] [require=region] [tx=slow] [suspend=tempradio]", "Err - set flood.filter[.n] <type> [hops] [path=blacklist] [scope=<name>] [require=region] [tx=slow] [suspend=tempradio]");
#endif
static const char FLOOD_PACKET_FILTER_DUPLICATE[] =
"Err - duplicate filter option";
@@ -3173,8 +3174,8 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
request_id, packet->radio_profile,
packet->radio_generation)) {
const char* error = deferred_cli_command.pending
? "Err - another remote command is still running"
: "Err - remote command is too long";
? MESH_CLI_REPLY_TEXT("Err - another remote command is still running", "Err - remote command busy")
: MESH_CLI_REPLY_TEXT("Err - remote command is too long", "Err - remote command too long");
remote_cli_reply_cache.remember(client->id.pub_key, request_id,
command_fingerprint, error);
sendRemoteCliReply(client, secret, packet->getPathHashSize(),
@@ -4926,15 +4927,15 @@ void MyMesh::addScheduledRadioParams(bool temporary, float freq, float bw, uint8
return;
}
if (start_time <= now) {
strcpy(reply, "Error: start is in the past");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error: start is in the past", "Error: start in past"));
return;
}
if (temporary && end_time <= now) {
strcpy(reply, "Error: end is in the past");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error: end is in the past", "Error: end in past"));
return;
}
if (temporary && end_time <= start_time) {
strcpy(reply, "Error: end must be after start");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error: end must be after start", "Error: end must follow start"));
return;
}
if (countScheduledRadioSettings(temporary) >= MAX_SCHEDULED_RADIO_SETTINGS_PER_TYPE) {
@@ -5054,7 +5055,7 @@ void MyMesh::formatScheduledRadioParams(bool temporary, const char* selector, ch
int wanted = 0;
if (!parsePositiveSelector(selector, wanted)) {
strcpy(reply, temporary ? "Error, use: get tempradioat [n]" : "Error, use: get radioat [n]");
strcpy(reply, temporary ? MESH_CLI_REPLY_TEXT("Error, use: get tempradioat [n]", "Error, get tempradioat [n]") : MESH_CLI_REPLY_TEXT("Error, use: get radioat [n]", "Error, get radioat [n]"));
return;
}
@@ -5077,7 +5078,7 @@ void MyMesh::deleteScheduledRadioParams(bool temporary, const char* selector, ch
if (!all) {
int wanted = 0;
if (!parsePositiveSelector(selector, wanted)) {
strcpy(reply, temporary ? "Error, use: del tempradioat [n]" : "Error, use: del radioat [n]"); return;
strcpy(reply, temporary ? MESH_CLI_REPLY_TEXT("Error, use: del tempradioat [n]", "Error, del tempradioat [n]") : MESH_CLI_REPLY_TEXT("Error, use: del radioat [n]", "Error, del radioat [n]")); return;
}
idx = findScheduledRadioSettingByIndex(temporary, wanted);
if (idx < 0) { strcpy(reply, "Error: not found"); return; }
@@ -5122,7 +5123,7 @@ void MyMesh::deleteScheduledRadioParams(bool temporary, const char* selector, ch
int wanted = 0;
if (!parsePositiveSelector(selector, wanted)) {
strcpy(reply, temporary ? "Error, use: del tempradioat [n]" : "Error, use: del radioat [n]");
strcpy(reply, temporary ? MESH_CLI_REPLY_TEXT("Error, use: del tempradioat [n]", "Error, del tempradioat [n]") : MESH_CLI_REPLY_TEXT("Error, use: del radioat [n]", "Error, del radioat [n]"));
return;
}
@@ -5696,7 +5697,7 @@ bool MyMesh::handleGuestCommand(const char* command, char* reply, size_t capacit
const char* value = command + 9;
while (*value == ' ' || *value == '\t') ++value;
if ((get && *value) || (set && strcmp(value, "on") && strcmp(value, "off"))) {
snprintf(reply, capacity, "Err - use get guest or set guest on|off");
snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("Err - use get guest or set guest on|off", "Err - get guest / set guest on|off"));
} else if (get) {
snprintf(reply, capacity, "> %s%s", guest_login.enabled() ? "on" : "off",
guest_login.healthy() ? "" : " (settings unavailable)");
@@ -6391,7 +6392,7 @@ void MyMesh::formatFloodPacketFilterBlacklist(const char* args, char* reply) con
uint8_t slot = 0;
if (!parseFloodFilterUnsigned(selector, FLOOD_PACKET_FILTER_BLACKLIST_MAX, slot)
|| slot == 0 || slot > flood_packet_filter_blacklist_count) {
snprintf(reply, 160, "Err - blacklist slot must be 1-%u",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - blacklist slot must be 1-%u", "Err - blacklist slot: 1-%u"),
(uint32_t)flood_packet_filter_blacklist_count);
return;
}
@@ -6437,7 +6438,7 @@ void MyMesh::setFloodPacketFilterBlacklist(const char* args, char* reply) {
size_t slot_len = (size_t)(cursor - slot_start);
char slot_text[8];
if (slot_len == 0 || slot_len >= sizeof(slot_text)) {
snprintf(reply, 160, "Err - blacklist slot must be 1-%u",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - blacklist slot must be 1-%u", "Err - blacklist slot: 1-%u"),
(uint32_t)FLOOD_PACKET_FILTER_BLACKLIST_MAX);
return;
}
@@ -6446,13 +6447,13 @@ void MyMesh::setFloodPacketFilterBlacklist(const char* args, char* reply) {
uint8_t slot = 0;
if (!parseFloodFilterUnsigned(slot_text, FLOOD_PACKET_FILTER_BLACKLIST_MAX, slot)
|| slot == 0) {
snprintf(reply, 160, "Err - blacklist slot must be 1-%u",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - blacklist slot must be 1-%u", "Err - blacklist slot: 1-%u"),
(uint32_t)FLOOD_PACKET_FILTER_BLACKLIST_MAX);
return;
}
requested_slot = slot - 1;
if (*cursor != ' ') {
strcpy(reply, "Err - expected six-digit repeater ID");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - expected six-digit repeater ID", "Err - repeater ID: six hex digits"));
return;
}
}
@@ -6463,10 +6464,10 @@ void MyMesh::setFloodPacketFilterBlacklist(const char* args, char* reply) {
uint8_t count = 0;
if (!parseFloodPacketFilterBlacklist(cursor, parsed, count)) {
if (requested_slot >= 0) {
snprintf(reply, 160, "Err - use 1-%d unique six-digit hex IDs separated by commas",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - use 1-%d unique six-digit hex IDs separated by commas", "Err - use 1-%d unique six-digit hex IDs, comma-separated"),
FLOOD_PACKET_FILTER_BLACKLIST_REPLACE_MAX);
} else {
snprintf(reply, 160, "Err - use 1-%d unique six-digit hex IDs separated by commas",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - use 1-%d unique six-digit hex IDs separated by commas", "Err - use 1-%d unique six-digit hex IDs, comma-separated"),
FLOOD_PACKET_FILTER_BLACKLIST_REPLACE_MAX);
}
return;
@@ -6478,12 +6479,12 @@ void MyMesh::setFloodPacketFilterBlacklist(const char* args, char* reply) {
if (requested_slot >= 0) {
if (requested_slot > flood_packet_filter_blacklist_count) {
snprintf(reply, 160, "Err - next blacklist slot is %u",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - next blacklist slot is %u", "Err - next blacklist slot: %u"),
(uint32_t)flood_packet_filter_blacklist_count + 1U);
return;
}
if ((uint16_t)requested_slot + count > FLOOD_PACKET_FILTER_BLACKLIST_MAX) {
snprintf(reply, 160, "Err - blacklist range exceeds slot %u",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - blacklist range exceeds slot %u", "Err - blacklist range past slot %u"),
(uint32_t)FLOOD_PACKET_FILTER_BLACKLIST_MAX);
return;
}
@@ -6513,7 +6514,7 @@ void MyMesh::setFloodPacketFilterBlacklist(const char* args, char* reply) {
if (!saveFloodPacketFilterBlacklist()) {
flood_packet_filter_blacklist_count = previous_count;
memcpy(flood_packet_filter_blacklist, previous, sizeof(previous));
strcpy(reply, "Err - unable to save flood filter blacklist");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter blacklist", "Err - flood filter blacklist not saved"));
return;
}
if (requested_slot >= 0) {
@@ -6538,7 +6539,7 @@ void MyMesh::deleteFloodPacketFilterBlacklist(const char* args, char* reply) {
if (!delete_all
&& (!parseFloodFilterUnsigned(selector, FLOOD_PACKET_FILTER_BLACKLIST_MAX, slot)
|| slot == 0 || slot > flood_packet_filter_blacklist_count)) {
snprintf(reply, 160, "Err - use: del flood.filter.blacklist[.<1-%u>| all]",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - use: del flood.filter.blacklist[.<1-%u>| all]", "Err - del flood.filter.blacklist[.<1-%u>| all]"),
(uint32_t)FLOOD_PACKET_FILTER_BLACKLIST_MAX);
return;
}
@@ -6564,7 +6565,7 @@ void MyMesh::deleteFloodPacketFilterBlacklist(const char* args, char* reply) {
if (!saveFloodPacketFilterBlacklist()) {
flood_packet_filter_blacklist_count = previous_count;
memcpy(flood_packet_filter_blacklist, previous, sizeof(previous));
strcpy(reply, "Err - unable to save flood filter blacklist");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter blacklist", "Err - flood filter blacklist not saved"));
return;
}
if (delete_all) {
@@ -8330,7 +8331,7 @@ void MyMesh::formatFloodPacketFilters(const char* args, char* reply, bool compac
}
uint8_t slot;
if (!parseFloodFilterUnsigned(selector, flood_packet_filter_slots, slot) || slot == 0) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
formatFloodPacketFilterDetail(slot - 1, reply, 160, compact);
@@ -8402,14 +8403,14 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
size_t slot_len = (size_t)(cursor - slot_start);
char slot_text[8];
if (slot_len == 0 || slot_len >= sizeof(slot_text)) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
memcpy(slot_text, slot_start, slot_len);
slot_text[slot_len] = 0;
uint8_t slot;
if (!parseFloodFilterUnsigned(slot_text, flood_packet_filter_slots, slot) || slot == 0) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
requested_slot = slot - 1;
@@ -8464,7 +8465,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
? tokens[0] + strlen("type=")
: floodFilterAsciiStartsWith(tokens[0], "t=") ? tokens[0] + 2 : tokens[0];
if (!parseFloodFilterPayloadType(type_text, payload_type)) {
strcpy(reply, "Err - packet type must be name, any, 0-15, or 0x00-0x0F");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - packet type must be name, any, 0-15, or 0x00-0x0F", "Err - type: name|any|0-15|0x00-0x0F"));
return;
}
@@ -8673,7 +8674,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
if (floodFilterAsciiEqual(tokens[i], "tx=slow")) {
scope_uses_slow_timing = true;
} else if (!floodFilterAsciiEqual(tokens[i], "tx=fast")) {
strcpy(reply, "Err - tx timing must be fast or slow");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - tx timing must be fast or slow", "Err - tx: fast|slow"));
return;
}
scope_timing_set = true;
@@ -8685,7 +8686,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
}
if (!normalizeFloodFilterScopeName(strchr(tokens[i], '=') + 1,
scope_name, sizeof(scope_name))) {
strcpy(reply, "Err - scope must be a public name of at most 30 characters");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - scope must be a public name of at most 30 characters", "Err - scope: public name, max 30 chars"));
return;
}
target_set = true;
@@ -8953,7 +8954,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
if (!saveFloodPacketFilters()) {
flood_packet_filters[slot] = previous;
flood_channel_data_rule_slot = previous_channel_data_slot;
strcpy(reply, "Err - unable to save flood filter");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter", "Err - flood filter not saved"));
return;
}
@@ -9003,7 +9004,7 @@ void MyMesh::formatFloodPacketFilters(const char* args, char* reply, bool compac
if (*selector != 0) {
uint8_t slot;
if (!parseFloodFilterUnsigned(selector, flood_packet_filter_slots, slot) || slot == 0) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
formatFloodPacketFilterDetail(slot - 1, reply, 160);
@@ -9055,19 +9056,19 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
size_t slot_len = (size_t)(cursor - slot_start);
char slot_text[8];
if (slot_len == 0 || slot_len >= sizeof(slot_text)) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
memcpy(slot_text, slot_start, slot_len);
slot_text[slot_len] = 0;
uint8_t slot;
if (!parseFloodFilterUnsigned(slot_text, flood_packet_filter_slots, slot) || slot == 0) {
snprintf(reply, 160, "Err - filter slot must be 1-%d", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - filter slot must be 1-%d", "Err - filter slot: 1-%d"), flood_packet_filter_slots);
return;
}
requested_slot = slot - 1;
if (*cursor != ' ') {
strcpy(reply, "Err - packet type must be name, any, 0-15, or 0x00-0x0F");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - packet type must be name, any, 0-15, or 0x00-0x0F", "Err - type: name|any|0-15|0x00-0x0F"));
return;
}
}
@@ -9078,7 +9079,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
char params[140];
#endif
if (strlen(cursor) >= sizeof(params)) {
strcpy(reply, "Err - filter parameters too long");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - filter parameters too long", "Err - filter params too long"));
return;
}
@@ -9088,7 +9089,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
char* token = params;
while (*token != 0) {
if (token_count >= 7) {
strcpy(reply, "Err - too many flood filter parameters");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - too many flood filter parameters", "Err - excess flood filter parameters"));
return;
}
tokens[token_count++] = token;
@@ -9105,7 +9106,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
uint8_t payload_type;
if (!parseFloodFilterPayloadType(tokens[0], payload_type)) {
strcpy(reply, "Err - packet type must be name, any, 0-15, or 0x00-0x0F");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - packet type must be name, any, 0-15, or 0x00-0x0F", "Err - type: name|any|0-15|0x00-0x0F"));
return;
}
@@ -9146,7 +9147,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
if (floodFilterAsciiEqual(tokens[i], "tx=slow")) {
scope_uses_slow_timing = true;
} else if (!floodFilterAsciiEqual(tokens[i], "tx=fast")) {
strcpy(reply, "Err - tx timing must be fast or slow");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - tx timing must be fast or slow", "Err - tx: fast|slow"));
return;
}
scope_timing_set = true;
@@ -9157,7 +9158,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
}
if (!normalizeFloodFilterScopeName(tokens[i] + strlen("scope="),
scope_name, sizeof(scope_name))) {
strcpy(reply, "Err - scope must be a public name of at most 30 characters");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - scope must be a public name of at most 30 characters", "Err - scope: public name, max 30 chars"));
return;
}
} else if (!hops_set && parseFloodFilterHopSpec(tokens[i], min_hops, max_hops)) {
@@ -9168,11 +9169,11 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
}
}
if (scope_requires_region_match && scope_name[0] == 0) {
strcpy(reply, "Err - require=region requires scope=<name>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - require=region requires scope=<name>", "Err - require=region needs scope=<name>"));
return;
}
if (scope_timing_set && scope_name[0] == 0) {
strcpy(reply, "Err - tx timing requires scope=<name>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - tx timing requires scope=<name>", "Err - tx needs scope=<name>"));
return;
}
@@ -9217,7 +9218,7 @@ void MyMesh::setFloodPacketFilter(const char* args, char* reply,
StrHelper::strzcpy(entry.scopeName(), scope_name, sizeof(entry.scopeName()));
if (!saveFloodPacketFilters()) {
entry = previous;
strcpy(reply, "Err - unable to save flood filter");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter", "Err - flood filter not saved"));
return;
}
@@ -9232,20 +9233,20 @@ void MyMesh::deleteFloodPacketFilter(const char* args, char* reply) {
if (*selector == '.') selector = skipFloodFilterSpaces(selector + 1);
if (floodFilterAsciiEqual(selector, "all")) {
if (!saveFloodPacketFilters(false, true)) {
strcpy(reply, "Err - unable to save flood filter");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter", "Err - flood filter not saved"));
} else {
if (flood_packet_filters) memset(flood_packet_filters, 0, sizeof(FloodPacketFilterEntry) * flood_packet_filter_slots);
#if MESH_ENABLE_FLOOD_RULE_ENGINE
flood_channel_data_rule_slot = 0xFF;
#endif
strcpy(reply, "OK - all flood filters removed");
strcpy(reply, MESH_CLI_REPLY_TEXT("OK - all flood filters removed", "OK - flood filters cleared"));
}
return;
}
uint8_t slot;
if (!parseFloodFilterUnsigned(selector, flood_packet_filter_slots, slot) || slot == 0) {
snprintf(reply, 160, "Err - use: del flood.filter.<1-%d>|all", flood_packet_filter_slots);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - use: del flood.filter.<1-%d>|all", "Err - del flood.filter.<1-%d>|all"), flood_packet_filter_slots);
return;
}
int index = slot - 1;
@@ -9266,7 +9267,7 @@ void MyMesh::deleteFloodPacketFilter(const char* args, char* reply) {
#if MESH_ENABLE_FLOOD_RULE_ENGINE
flood_channel_data_rule_slot = previous_channel_data_slot;
#endif
strcpy(reply, "Err - unable to save flood filter");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unable to save flood filter", "Err - flood filter not saved"));
} else {
strcpy(reply, "OK");
}
@@ -9821,7 +9822,7 @@ static bool parseFloodChannelScopeIndex(const char*& cursor, int& index) {
void MyMesh::formatFloodChannelScopeDetail(int index, char* reply, size_t reply_len) {
if (index < 0 || index >= FLOOD_CHANNEL_SCOPE_SLOTS) {
snprintf(reply, reply_len, "Err - scope slot must be 1-%d", FLOOD_CHANNEL_SCOPE_SLOTS);
snprintf(reply, reply_len, MESH_CLI_REPLY_TEXT("Err - scope slot must be 1-%d", "Err - scope slot: 1-%d"), FLOOD_CHANNEL_SCOPE_SLOTS);
return;
}
const auto& entry = floodChannelScopes()[index];
@@ -9879,7 +9880,7 @@ void MyMesh::formatFloodChannelScopes(const char* args, char* reply) {
const char* cursor = args;
int index = -1;
if (!parseFloodChannelScopeIndex(cursor, index)) {
snprintf(reply, 160, "Err - scope slot must be 1-%d", FLOOD_CHANNEL_SCOPE_SLOTS);
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - scope slot must be 1-%d", "Err - scope slot: 1-%d"), FLOOD_CHANNEL_SCOPE_SLOTS);
return;
}
if (*cursor != 0) {
@@ -9956,7 +9957,7 @@ void MyMesh::setFloodChannelScope(const char* args, char* reply) {
option + (option[0] == 'p' && option[1] == '=' ? 3 : 12),
FLOOD_RETRY_BRIDGE_BUCKETS, bucket)
|| bucket == 0) {
strcpy(reply, "Err - path bucket must be 1-6");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - path bucket must be 1-6", "Err - path bucket: 1-6"));
return;
}
path_set = true;
@@ -9981,7 +9982,7 @@ void MyMesh::setFloodChannelScope(const char* args, char* reply) {
target_text + strlen("scope="), direct_scope_name,
sizeof(direct_scope_name))) {
strcpy(reply,
"Err - scope must be a public name of at most 30 characters");
MESH_CLI_REPLY_TEXT("Err - scope must be a public name of at most 30 characters", "Err - scope: public name, max 30 chars"));
return;
}
} else {
@@ -10151,7 +10152,7 @@ void MyMesh::deleteFloodChannelScope(const char* args, char* reply) {
void MyMesh::formatFloodChannelScopeRequirementDetail(
int index, char* reply, size_t reply_len) const {
if (index < 0 || index >= FLOOD_CHANNEL_SCOPE_REQUIRE_SLOTS) {
snprintf(reply, reply_len, "Err - require slot must be 1-%d",
snprintf(reply, reply_len, MESH_CLI_REPLY_TEXT("Err - require slot must be 1-%d", "Err - require slot: 1-%d"),
FLOOD_CHANNEL_SCOPE_REQUIRE_SLOTS);
return;
}
@@ -10174,7 +10175,7 @@ void MyMesh::formatFloodChannelScopeRequirements(const char* args,
const char* cursor = args;
int index = -1;
if (!parseFloodChannelScopeIndex(cursor, index)) {
snprintf(reply, 160, "Err - require slot must be 1-%d",
snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - require slot must be 1-%d", "Err - require slot: 1-%d"),
FLOOD_CHANNEL_SCOPE_REQUIRE_SLOTS);
return;
}
@@ -10219,7 +10220,7 @@ void MyMesh::setFloodChannelScopeRequirement(const char* args, char* reply) {
char ignored_name[FLOOD_GROUP_MODERATION_NAME_LEN];
if (!parseFloodModerationChannel(channel_text, secret, key_len, hash_prefix,
ignored_name, sizeof(ignored_name))) {
strcpy(reply, "Err - channel must be public, #name, or 128/256-bit key");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - channel must be public, #name, or 128/256-bit key", "Err - channel: public|#name|128/256-bit key"));
return;
}
@@ -12474,7 +12475,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
}
if (region_load_active && sender && !sender->isAdmin() && !sender->isRegionMgr()) {
strcpy(reply, "Err - region load owned by admin/region manager");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - region load owned by admin/region manager", "Err - region load: admin/region manager only"));
return;
}
@@ -13194,7 +13195,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
&& mesh::Utils::fromHex(pubkey, (int)(hex_len / 2), hex)) {
uint32_t perms;
if (!mesh::cli::parseUnsignedIntegerStrict(sp, perms) || perms > UINT8_MAX) {
strcpy(reply, "Err - permissions must be 0-255");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - permissions must be 0-255", "Err - permissions: 0-255"));
} else if (acl.applyPermissions(self_id, pubkey, (int)(hex_len / 2), static_cast<uint8_t>(perms))) {
mesh::scheduleLazyPersistenceMutation(
dirty_contacts_expiry, contacts_save_failures,
@@ -13240,7 +13241,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
} else if (strncmp(command, "send text.flood ", 16) == 0) {
char* text = trimSpaces(command + 16);
if (*text == 0) {
strcpy(reply, "Err - usage: send text.flood <message>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - usage: send text.flood <message>", "Err - send text.flood <message>"));
} else if (sendRepeatersFloodText(text)) {
strcpy(reply, "OK");
} else {
@@ -13275,7 +13276,7 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
snprintf(reply, 160, "OK - saved off; rx.watchdog=%s",
radio_timing.watchdogLabel(_prefs.rx_watchdog_enabled));
} else {
strcpy(reply, "Err - usage: set rx.watchdog <on|off>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - usage: set rx.watchdog <on|off>", "Err - set rx.watchdog <on|off>"));
}
} else if (strncmp(command, "set battery.alert ", 18) == 0) {
const char* value = command + 18;
@@ -13286,22 +13287,22 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
if (*region_name) {
if (strchr(region_name, ' ') != NULL) {
strcpy(reply, "Err - region names cannot contain spaces");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - region names cannot contain spaces", "Err - region name: no spaces"));
return;
}
region = region_map.findByName(region_name);
if (region == NULL || region->isWildcard()) {
strcpy(reply, "Err - unknown or invalid alert region");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - unknown or invalid alert region", "Err - unknown/invalid alert region"));
return;
}
} else {
region = findNarrowestBatteryAlertRegion(ambiguous);
if (region == NULL) {
strcpy(reply, "Err - define a usable region before enabling battery alerts");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - define a usable region before enabling battery alerts", "Err - battery alerts need a usable region first"));
return;
}
if (ambiguous) {
strcpy(reply, "Err - multiple narrowest regions; specify one");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - multiple narrowest regions; specify one", "Err - specify one of the narrowest regions"));
return;
}
}
@@ -13324,14 +13325,14 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Err - usage: set battery.alert <on [region]|off>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - usage: set battery.alert <on [region]|off>", "Err - set battery.alert <on [region]|off>"));
}
} else if (strncmp(command, "set battery.alert.low ", 22) == 0) {
uint8_t percent;
if (!parseBatteryAlertPercent(command + 22, 1, 100, percent)) {
strcpy(reply, "Err - usage: set battery.alert.low <1-100>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - usage: set battery.alert.low <1-100>", "Err - set battery.alert.low <1-100>"));
} else if (percent <= _prefs.battery_alert_critical_percent) {
strcpy(reply, "Err - low must be greater than critical");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - low must be greater than critical", "Err - low must exceed critical"));
} else {
_prefs.battery_alert_low_percent = percent;
next_battery_alert_check = 0;
@@ -13341,9 +13342,9 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
} else if (strncmp(command, "set battery.alert.critical ", 27) == 0) {
uint8_t percent;
if (!parseBatteryAlertPercent(command + 27, 0, 99, percent)) {
strcpy(reply, "Err - usage: set battery.alert.critical <0-99>");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - usage: set battery.alert.critical <0-99>", "Err - set battery.alert.critical <0-99>"));
} else if (percent >= _prefs.battery_alert_low_percent) {
strcpy(reply, "Err - critical must be less than low");
strcpy(reply, MESH_CLI_REPLY_TEXT("Err - critical must be less than low", "Err - critical must be below low"));
} else {
_prefs.battery_alert_critical_percent = percent;
next_battery_alert_check = 0;
+1
View File
@@ -1,5 +1,6 @@
#pragma once
#include "CLIReplyText.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
+14
View File
@@ -0,0 +1,14 @@
#pragma once
// Constrained profiles may shorten human CLI prose. Command syntax, structured
// replies, protocol acknowledgements, destructive and rollback warnings keep
// their exact text.
#ifndef MESH_CLI_COMPACT_REPLIES
#define MESH_CLI_COMPACT_REPLIES 0
#endif
#if MESH_CLI_COMPACT_REPLIES
#define MESH_CLI_REPLY_TEXT(verbose, concise) concise
#else
#define MESH_CLI_REPLY_TEXT(verbose, concise) verbose
#endif
+71 -70
View File
@@ -1,3 +1,4 @@
#include "CLIReplyText.h"
#include <helpers/RegulatorSettings.h>
#if defined(USE_LR2021)
#include <helpers/radiolib/LR2021CalibrationSettings.h>
@@ -262,7 +263,7 @@ bool CommonCLI::applyAndSavePrimaryRadioParams(float freq, float bw, uint8_t sf,
uint8_t cr, uint16_t preamble, char* reply) {
const int8_t previous_power = _prefs->tx_power_dbm;
if (!savePrimaryRadioParams(freq, bw, sf, cr, preamble)) {
mesh::cli::formatError(reply, 160, "radio settings could not be saved");
mesh::cli::formatError(reply, 160, MESH_CLI_REPLY_TEXT("radio settings could not be saved", "radio settings not saved"));
return false;
}
// Arm the role's reply handoff now, while the originating CLI command is
@@ -3691,7 +3692,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
_callbacks->onManualClockSet();
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
sprintf(reply, MESH_CLI_REPLY_TEXT("OK - clock set: %02d:%02d - %d/%d/%d UTC", "OK - clock: %02d:%02d - %d/%d/%d UTC"), dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else {
strcpy(reply, "ERR: clock cannot go backwards");
}
@@ -3866,7 +3867,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
_callbacks->onManualClockSet();
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
sprintf(reply, "OK - clock set: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
sprintf(reply, MESH_CLI_REPLY_TEXT("OK - clock set: %02d:%02d - %d/%d/%d UTC", "OK - clock: %02d:%02d - %d/%d/%d UTC"), dt.hour(), dt.minute(), dt.day(), dt.month(), dt.year());
} else {
strcpy(reply, "(ERR: clock cannot go backwards)");
}
@@ -3900,7 +3901,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
} else if (memcmp(command, "tempradio ", 10) == 0) {
uint16_t preamble = 0;
if (!mesh::RadioProfileCLI::parseSuffix(command + 10, 5, tmp, sizeof(tmp), preamble)) {
strcpy(reply, "Error, use: tempradio f,bw,sf,cr,minutes[,preamble]"); return;
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use: tempradio f,bw,sf,cr,minutes[,preamble]", "Error, tempradio f,bw,sf,cr,minutes[,preamble]")); return;
}
const char *parts[5];
int num = mesh::Utils::parseTextParts(tmp, parts, 5);
@@ -3965,7 +3966,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
_callbacks->clearRecentRepeaters();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, unsupported on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, unsupported on this role", "Error, unsupported role"));
}
} else if (memcmp(command, "get ", 4) == 0) {
handleGetCmd(sender_timestamp, command, reply);
@@ -4047,7 +4048,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
}
} else if (memcmp(command, "sensor set ", 11) == 0) {
if (!copyCLIArgument(tmp, sizeof(tmp), &command[11])) {
strcpy(reply, "Error, argument too long");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, argument too long", "Error, arg too long"));
return;
}
const char *parts[2];
@@ -4391,7 +4392,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0,1, or 2");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0,1, or 2", "Error, 0|1|2"));
}
return;
}
@@ -4781,18 +4782,18 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
}
#endif
if (isAdvancedRetryConfig(config) && !_callbacks->supportsAdvancedRetryConfig()) {
strcpy(reply, "Error, unsupported on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, unsupported on this role", "Error, unsupported role"));
return;
}
if (isBasicRetryConfig(config) && !_callbacks->supportsBasicRetryConfig()) {
strcpy(reply, "Error, retry configuration unsupported on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, retry configuration unsupported on this role", "Error, retry unsupported for role"));
return;
}
if (memcmp(config, "dutycycle ", 10) == 0) {
float dc = 0.0f;
if (!mesh::cli::parseDecimalStrict(&config[10], dc)
|| dc < 1.0f || dc > 100.0f) {
strcpy(reply, "ERROR: dutycycle must be 1-100");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERROR: dutycycle must be 1-100", "ERROR: dutycycle 1-100"));
} else {
_prefs->airtime_factor = (100.0f / dc) - 1.0f;
savePrefs();
@@ -4816,7 +4817,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
mesh::cli::formatError(reply, 160, "use set system.watchdog on|off");
}
#else
mesh::cli::formatError(reply, 160, "unsupported on this platform");
mesh::cli::formatError(reply, 160, MESH_CLI_REPLY_TEXT("unsupported on this platform", "platform unsupported"));
#endif
} else if (memcmp(config, "af ", 3) == 0) {
float factor = 0.0f;
@@ -4825,7 +4826,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, invalid airtime factor");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid airtime factor", "Error, bad airtime factor"));
}
} else if (memcmp(config, "int.thresh ", 11) == 0) {
_prefs->interference_threshold = atoi(&config[11]);
@@ -4847,7 +4848,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
_prefs->agc_reset_interval = seconds / 4;
savePrefs();
sprintf(reply, "OK - interval rounded to %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
sprintf(reply, MESH_CLI_REPLY_TEXT("OK - interval rounded to %d", "OK - rounded interval %d"), ((uint32_t) _prefs->agc_reset_interval) * 4);
}
} else if (memcmp(config, "multi.acks ", 11) == 0) {
_prefs->multi_acks = atoi(&config[11]);
@@ -4867,7 +4868,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "ERROR: telemetry.access must be all or acl");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERROR: telemetry.access must be all or acl", "ERROR: telemetry.access all|acl"));
}
} else if (memcmp(config, "flood.advert.interval ", 22) == 0) {
uint32_t hours = 0;
@@ -4889,7 +4890,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
uint32_t mins = 0;
if (!parseUint32Strict(&config[16], mins)
|| (mins != 0 && (mins < MIN_LOCAL_ADVERT_INTERVAL || mins > 240))) {
sprintf(reply, "Error: interval must be 0 (off) or %d-240 minutes", MIN_LOCAL_ADVERT_INTERVAL);
sprintf(reply, MESH_CLI_REPLY_TEXT("Error: interval must be 0 (off) or %d-240 minutes", "Error: interval 0 (off) or %d-240 minutes"), MIN_LOCAL_ADVERT_INTERVAL);
} else {
const uint8_t previous = _prefs->advert_interval;
_prefs->advert_interval = (uint8_t)(mins / 2);
@@ -4940,9 +4941,9 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_prefs->disable_fwd = strcmp(&config[7], "off") == 0;
savePrefs();
_callbacks->onRetryConfigChanged();
strcpy(reply, _prefs->disable_fwd ? "OK - repeat is now OFF" : "OK - repeat is now ON");
strcpy(reply, _prefs->disable_fwd ? MESH_CLI_REPLY_TEXT("OK - repeat is now OFF", "OK - repeat OFF") : MESH_CLI_REPLY_TEXT("OK - repeat is now ON", "OK - repeat ON"));
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "radio.rxgain ", 13) == 0) {
const char* value = &config[13];
@@ -4984,7 +4985,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (changed) _callbacks->recalibrateNoiseFloor();
_prefs->radio_fem_rxgain = 1;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain on");
strcpy(reply, MESH_CLI_REPLY_TEXT("OK - LoRa FEM RX gain on", "OK - FEM RX gain on"));
} else {
mesh::cli::formatError(reply, 160, "failed to apply LoRa FEM RX gain");
}
@@ -4994,7 +4995,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (changed) _callbacks->recalibrateNoiseFloor();
_prefs->radio_fem_rxgain = 0;
savePrefs();
strcpy(reply, "OK - LoRa FEM RX gain off");
strcpy(reply, MESH_CLI_REPLY_TEXT("OK - LoRa FEM RX gain off", "OK - FEM RX gain off"));
} else {
mesh::cli::formatError(reply, 160, "failed to apply LoRa FEM RX gain");
}
@@ -5013,7 +5014,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (_board->setLoRaFemPaGainEnabled(true)) {
_prefs->radio_fem_txgain = 1;
savePrefs();
strcpy(reply, "OK - LoRa FEM TX gain on");
strcpy(reply, MESH_CLI_REPLY_TEXT("OK - LoRa FEM TX gain on", "OK - FEM TX gain on"));
} else {
mesh::cli::formatError(reply, 160, "failed to apply LoRa FEM TX gain");
}
@@ -5021,7 +5022,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (_board->setLoRaFemPaGainEnabled(false)) {
_prefs->radio_fem_txgain = 0;
savePrefs();
strcpy(reply, "OK - LoRa FEM TX gain off");
strcpy(reply, MESH_CLI_REPLY_TEXT("OK - LoRa FEM TX gain off", "OK - FEM TX gain off"));
} else {
mesh::cli::formatError(reply, 160, "failed to apply LoRa FEM TX gain");
}
@@ -5144,14 +5145,14 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (manual_timings_requested
&& (rx_us < RX_POWERSAVING_MIN_MANUAL_PERIOD_US
|| sleep_us < RX_POWERSAVING_MIN_MANUAL_PERIOD_US)) {
sprintf(reply, "ERROR: range is %lu-%lu us",
sprintf(reply, MESH_CLI_REPLY_TEXT("ERROR: range is %lu-%lu us", "ERROR: range %lu-%lu us"),
(unsigned long)RX_POWERSAVING_MIN_MANUAL_PERIOD_US,
(unsigned long)RX_POWERSAVING_MAX_PERIOD_US);
return;
}
if (!isValidRxPowerSavingPeriod(rx_us) || !isValidRxPowerSavingPeriod(sleep_us)) {
sprintf(reply, "ERROR: range is %lu-%lu us",
sprintf(reply, MESH_CLI_REPLY_TEXT("ERROR: range is %lu-%lu us", "ERROR: range %lu-%lu us"),
(unsigned long)RX_POWERSAVING_MIN_MANUAL_PERIOD_US,
(unsigned long)RX_POWERSAVING_MAX_PERIOD_US);
return;
@@ -5223,7 +5224,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
appendRxPowerSavingAdjustmentNote(reply, _prefs, sf, bw);
_radio_profiles.appendPrimaryChirpWarning(reply, 160, sf, bw, preamble);
} else {
strcpy(reply, "Error, invalid radio params");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid radio params", "Error, bad radio params"));
}
} else if (memcmp(config, "radioat ", 8) == 0) {
float freq, bw;
@@ -5233,9 +5234,9 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
char legacy[120];
if (!mesh::RadioProfileCLI::parseSuffix(&config[8], 5, legacy, sizeof(legacy), preamble)
|| !parseScheduledRadioArgs(legacy, false, freq, bw, sf, cr, start_time, end_time, _rtc->getCurrentTime())) {
strcpy(reply, "Error, use: set radioat f,bw,sf,cr,start (UTC or +minutes)");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use: set radioat f,bw,sf,cr,start (UTC or +minutes)", "Error, set radioat f,bw,sf,cr,start (UTC or +minutes)"));
} else if (!acceptsPrimaryRadioParams(freq, bw, sf, cr, preamble)) {
strcpy(reply, "Error, invalid radio params");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid radio params", "Error, bad radio params"));
} else {
_callbacks->addScheduledRadioParams(false, freq, bw, sf, cr, start_time, end_time, reply, preamble);
if (strncmp(reply, "OK", 2) == 0) {
@@ -5251,9 +5252,9 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
char legacy[120];
if (!mesh::RadioProfileCLI::parseSuffix(&config[12], 6, legacy, sizeof(legacy), preamble)
|| !parseScheduledRadioArgs(legacy, true, freq, bw, sf, cr, start_time, end_time, _rtc->getCurrentTime())) {
strcpy(reply, "Error, use: set tempradioat f,bw,sf,cr,start,end (UTC or +minutes)");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use: set tempradioat f,bw,sf,cr,start,end (UTC or +minutes)", "Error, set tempradioat f,bw,sf,cr,start,end (UTC or +minutes)"));
} else if (!acceptsPrimaryRadioParams(freq, bw, sf, cr, preamble)) {
strcpy(reply, "Error, invalid radio params");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid radio params", "Error, bad radio params"));
} else {
_callbacks->addScheduledRadioParams(true, freq, bw, sf, cr, start_time, end_time, reply, preamble);
if (strncmp(reply, "OK", 2) == 0) {
@@ -5268,7 +5269,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, invalid latitude");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid latitude", "Error, bad latitude"));
}
} else if (memcmp(config, "lon ", 4) == 0) {
float longitude = 0.0f;
@@ -5277,7 +5278,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, invalid longitude");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid longitude", "Error, bad longitude"));
}
} else if (memcmp(config, "rxdelay ", 8) == 0) {
float db = 0.0f;
@@ -5287,7 +5288,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-20");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-20", "Error, 0-20"));
}
} else if (memcmp(config, "txdelay ", 8) == 0) {
float f = 0.0f;
@@ -5297,7 +5298,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-2");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-2", "Error, 0-2"));
}
} else if (memcmp(config, "flood.max.unscoped ", 19) == 0) {
uint32_t m;
@@ -5334,7 +5335,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-2");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-2", "Error, 0-2"));
}
} else if (memcmp(config, "retry.preset ", 13) == 0) {
uint8_t preset;
@@ -5343,7 +5344,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
sprintf(reply, "OK - %s", retryPresetName(_prefs->retry_preset));
} else {
strcpy(reply, "Error, must be infra, rooftop, or mobile");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be infra, rooftop, or mobile", "Error, infra|rooftop|mobile"));
}
} else if (memcmp(config, "direct.retry ", 13) == 0) {
if (strcmp(&config[13], "on") == 0) {
@@ -5357,7 +5358,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_callbacks->onRetryConfigChanged();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "direct.retry.heard ", 19) == 0) {
if (strcmp(&config[19], "on") == 0) {
@@ -5369,11 +5370,11 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "direct.retry.margin ", 20) == 0) {
if (!looksNumeric(&config[20])) {
strcpy(reply, "Error, must be 0-40 dB");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-40 dB", "Error, 0-40 dB"));
} else {
int16_t margin_x4 = parseSnrDbX4(&config[20]);
if (margin_x4 >= 0 && margin_x4 <= 160) {
@@ -5382,7 +5383,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-40 dB");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-40 dB", "Error, 0-40 dB"));
}
}
} else if (memcmp(config, "direct.retry.count ", 19) == 0) {
@@ -5393,7 +5394,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 1-15");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 1-15", "Error, 1-15"));
}
} else if (memcmp(config, "direct.retry.base ", 18) == 0) {
int base_ms = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1;
@@ -5403,7 +5404,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 10-5000 ms");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 10-5000 ms", "Error, 10-5000 ms"));
}
} else if (memcmp(config, "direct.retry.step ", 18) == 0) {
int step_ms = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1;
@@ -5413,7 +5414,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-5000 ms");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-5000 ms", "Error, 0-5000 ms"));
}
} else if (memcmp(config, "flood.channel.data.hops ", 24) == 0) {
uint8_t max_hops;
@@ -5422,7 +5423,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be all or 1-7");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be all or 1-7", "Error, all|1-7"));
}
} else if (memcmp(config, "flood.channel.data ", 19) == 0) {
if (strcmp(&config[19], "on") == 0) {
@@ -5434,7 +5435,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "flood.retry.count ", 18) == 0) {
int attempts = looksUnsignedInteger(&config[18]) ? _atoi(&config[18]) : -1;
@@ -5445,7 +5446,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
_callbacks->onRetryConfigChanged();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-15");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-15", "Error, 0-15"));
}
} else if (memcmp(config, "flood.retry.path ", 17) == 0) {
uint8_t path_gate;
@@ -5458,7 +5459,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-63 or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-63 or off", "Error, 0-63|off"));
}
} else if (memcmp(config, "flood.retry.group.path ", 23) == 0) {
uint8_t path_gate;
@@ -5470,7 +5471,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be 0-63 or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be 0-63 or off", "Error, 0-63|off"));
}
} else if (memcmp(config, "flood.retry.prefixes ", 21) == 0) {
if (parseFloodRetryPrefixList(_prefs->flood_retry_prefixes, FLOOD_RETRY_PREFIX_SLOTS, &config[21])) {
@@ -5479,7 +5480,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "OK");
}
} else {
sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes",
sprintf(reply, MESH_CLI_REPLY_TEXT("Error, use up to %u comma-separated 3-byte hex prefixes", "Error, max %u comma-separated 3-byte hex prefixes"),
(unsigned int)FLOOD_RETRY_PREFIX_SLOTS);
}
} else if (memcmp(config, "flood.retry.ignore ", 19) == 0) {
@@ -5488,7 +5489,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes",
sprintf(reply, MESH_CLI_REPLY_TEXT("Error, use up to %u comma-separated 3-byte hex prefixes", "Error, max %u comma-separated 3-byte hex prefixes"),
(unsigned int)FLOOD_RETRY_IGNORE_PREFIXES);
}
} else if (memcmp(config, "flood.retry.advert ", 19) == 0) {
@@ -5501,7 +5502,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "flood.retry.bridge ", 19) == 0) {
if (strcmp(&config[19], "on") == 0) {
@@ -5519,7 +5520,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, must be on or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be on or off", "Error, on|off"));
}
} else if (memcmp(config, "flood.retry.bucket ", 19) == 0) {
const char* params = &config[19];
@@ -5536,12 +5537,12 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
strcpy(reply, "OK");
}
} else {
sprintf(reply, "Error, use up to %u comma-separated 3-byte hex prefixes",
sprintf(reply, MESH_CLI_REPLY_TEXT("Error, use up to %u comma-separated 3-byte hex prefixes", "Error, max %u comma-separated 3-byte hex prefixes"),
(unsigned int)FLOOD_RETRY_BUCKET_PREFIXES);
}
} else if (memcmp(config, "direct.retry.cr ", 16) == 0) {
if (!copyCLIArgument(tmp, sizeof(tmp), &config[16])) {
strcpy(reply, "Error, argument too long");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, argument too long", "Error, arg too long"));
return;
}
if (strcmp(&config[16], "off") == 0) {
@@ -5554,7 +5555,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, use on or off on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use on or off on this role", "Error, on|off"));
}
} else {
const char *parts[4];
@@ -5573,15 +5574,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error, SNR must fit -32.00..31.75 dB");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, SNR must fit -32.00..31.75 dB", "Error, SNR -32.00..31.75 dB"));
}
} else {
strcpy(reply, "Error, use CR4,CR5,CR7,CR8 SNRs or off");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use CR4,CR5,CR7,CR8 SNRs or off", "Error, CR4,CR5,CR7,CR8 SNRs|off"));
}
}
} else if (memcmp(config, "recent.repeater ", 16) == 0) {
if (!copyCLIArgument(tmp, sizeof(tmp), &config[16])) {
strcpy(reply, "Error, argument too long");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, argument too long", "Error, arg too long"));
return;
}
const char *parts[2];
@@ -5590,15 +5591,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
uint8_t prefix_len = 0;
int16_t snr_x4 = 12; // default to +3.0 dB when omitted or invalid
if (num < 1 || !parseHashPrefix(parts[0], prefix, prefix_len)) {
strcpy(reply, "Error, use: set recent.repeater <hex> [snr_db]");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use: set recent.repeater <hex> [snr_db]", "Error, set recent.repeater <hex> [snr_db]"));
} else if (num > 1 && !looksNumeric(parts[1])) {
strcpy(reply, "Error, SNR must be numeric");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, SNR must be numeric", "Error, numeric SNR needed"));
} else {
if (num > 1) {
snr_x4 = parseSnrDbX4(parts[1]);
}
if (snr_x4 < -128 || snr_x4 > 127) {
strcpy(reply, "Error, SNR must fit -32.00..31.75 dB");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, SNR must fit -32.00..31.75 dB", "Error, SNR -32.00..31.75 dB"));
} else if (_callbacks->setRecentRepeater(prefix, prefix_len, (int8_t)snr_x4)) {
char prefix_hex[RECENT_REPEATER_PREFIX_MAX_BYTES * 2 + 1];
char snr[12];
@@ -5607,7 +5608,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
formatSnrDbX4Short(snr, sizeof(snr), snr_x4);
sprintf(reply, "OK - set %s at %s SNR", prefix_hex, snr);
} else {
strcpy(reply, "Error, table rejected prefix");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, table rejected prefix", "Error, prefix rejected"));
}
}
} else if (memcmp(config, "owner.info ", 11) == 0) {
@@ -5633,7 +5634,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
mode = LOOP_DETECT_STRICT;
} else {
mode = 0xFF;
strcpy(reply, "Error, must be: off, minimal, moderate, or strict");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, must be: off, minimal, moderate, or strict", "Error, off|minimal|moderate|strict"));
}
if (mode != 0xFF) {
_prefs->loop_detect = mode;
@@ -5647,7 +5648,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
if (!mesh::cli::parseIntegerStrict(&config[3], power)) {
mesh::cli::formatError(reply, 160, "invalid TX power");
} else if (!mesh::isLoRaTxPowerValid(power, _prefs->freq)) {
sprintf(reply, "Error: TX power range is %d to %d dBm",
sprintf(reply, MESH_CLI_REPLY_TEXT("Error: TX power range is %d to %d dBm", "Error: TX power %d..%d dBm"),
(int)minimum, (int)maximum);
} else {
applyAndSaveTxPower(static_cast<int8_t>(power), reply);
@@ -5659,7 +5660,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
applyAndSavePrimaryRadioParams(freq, _prefs->bw, _prefs->sf, _prefs->cr,
_prefs->primary_radio_preamble, reply);
} else {
strcpy(reply, "Error, invalid frequency");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, invalid frequency", "Error, bad frequency"));
}
#ifdef WITH_BRIDGE
} else if (memcmp(config, "bridge.enabled ", 15) == 0) {
@@ -6167,11 +6168,11 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
}
#endif
if (isAdvancedRetryConfig(config) && !_callbacks->supportsAdvancedRetryConfig()) {
strcpy(reply, "Error, unsupported on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, unsupported on this role", "Error, unsupported role"));
return;
}
if (isBasicRetryConfig(config) && !_callbacks->supportsBasicRetryConfig()) {
strcpy(reply, "Error, retry configuration unsupported on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, retry configuration unsupported on this role", "Error, retry unsupported for role"));
return;
}
mesh::cli::RecentRepeaterGetQuery recent_query;
@@ -6186,7 +6187,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#if defined(NRF52_PLATFORM)
sprintf(reply, "> %s", _prefs->system_watchdog_enabled ? "on" : "off");
#else
mesh::cli::formatError(reply, 160, "unsupported on this platform");
mesh::cli::formatError(reply, 160, MESH_CLI_REPLY_TEXT("unsupported on this platform", "platform unsupported"));
#endif
} else if (configKeyEquals(config, "af")) {
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
@@ -6357,7 +6358,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
recent_query.search_prefix_len == 0 ? NULL : recent_query.search_prefix,
recent_query.search_prefix_len);
} else if (recent_query_match == mesh::cli::RecentRepeaterGetMatch::Invalid) {
strcpy(reply, "Error, use: get recent.repeaters [page] | search <2|4|6 hex> [page]");
strcpy(reply, MESH_CLI_REPLY_TEXT("Error, use: get recent.repeaters [page] | search <2|4|6 hex> [page]", "Error, get recent.repeaters [page] | search <2|4|6 hex> [page]"));
} else if (configKeyEquals(config, "owner.info")) {
*reply++ = '>';
*reply++ = ' ';
@@ -6513,7 +6514,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#ifdef NRF52_POWER_MANAGEMENT
strcpy(reply, _board->isExternalPowered() ? "> external" : "> battery");
#else
strcpy(reply, "ERROR: Power management not supported");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERROR: Power management not supported", "ERROR: Power management unsupported"));
#endif
} else if (configKeyEquals(config, "pwrmgt.bootreason")) {
sprintf(reply, "> Reset: %s; Shutdown: %s",
@@ -6523,7 +6524,7 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
#ifdef NRF52_POWER_MANAGEMENT
sprintf(reply, "> %u mV", _board->getBootVoltage());
#else
strcpy(reply, "ERROR: Power management not supported");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERROR: Power management not supported", "ERROR: Power management unsupported"));
#endif
} else if (configKeyEquals(config, "reboot.interval")) {
if (_prefs->reboot_interval == 0) {
@@ -6608,7 +6609,7 @@ static bool processRegionDefSegment(RegionMap* map, char* tok, RegionEntry** cur
if (jump) {
RegionEntry* j = map->findByNamePrefix(jump);
if (j == NULL) { snprintf(reply, 160, "Err - unknown or ambiguous jump: %s", jump); return false; }
if (j == NULL) { snprintf(reply, 160, MESH_CLI_REPLY_TEXT("Err - unknown or ambiguous jump: %s", "Err - unknown/ambiguous jump: %s"), jump); return false; }
*cursor = j;
} else {
*cursor = r;
+6 -4
View File
@@ -1,5 +1,7 @@
#pragma once
#include "CLIReplyText.h"
#include "FloodAdvertLimiter.h"
#include "FloodCommandAliases.h"
#include <Utils.h>
@@ -43,7 +45,7 @@ inline bool handleFloodAdvertGet(FloodAdvertLimiter* limiter, const char* comman
return true;
}
if (!limiter) {
strcpy(reply, "ERR: advert limiter unavailable on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERR: advert limiter unavailable on this role", "ERR: advert limiter unavailable for role"));
return true;
}
@@ -54,7 +56,7 @@ inline bool handleFloodAdvertGet(FloodAdvertLimiter* limiter, const char* comman
if (total == 0 && !detail && number == 1) {
strcpy(reply, "> no rate-limited adverts");
} else if (offset >= total) {
strcpy(reply, "ERR: advert list index out of range");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERR: advert list index out of range", "ERR: advert index out of range"));
} else if (detail) {
const auto& row = rows[0];
char key[PUB_KEY_SIZE * 2 + 1];
@@ -100,11 +102,11 @@ inline bool handleFloodAdvertClear(FloodAdvertLimiter* limiter, const char* comm
if (*end || !valid) {
strcpy(reply, "ERR: clear flood.advert all|<64-hex-key>");
} else if (!limiter) {
strcpy(reply, "ERR: advert limiter unavailable on this role");
strcpy(reply, MESH_CLI_REPLY_TEXT("ERR: advert limiter unavailable on this role", "ERR: advert limiter unavailable for role"));
} else {
if (all) limiter->reset();
else limiter->clear(key);
strcpy(reply, all ? "OK - all advert limit history cleared" : "OK - key advert limit history cleared");
strcpy(reply, all ? MESH_CLI_REPLY_TEXT("OK - all advert limit history cleared", "OK - all advert history cleared") : MESH_CLI_REPLY_TEXT("OK - key advert limit history cleared", "OK - key advert history cleared"));
}
return true;
}
+3 -2
View File
@@ -1,3 +1,4 @@
#include "CLIReplyText.h"
#include "LedSettings.h"
#include "CheckedSettingsFile.h"
#include <MeshCore.h>
@@ -80,12 +81,12 @@ bool handleCommand(const char* command, char* reply, size_t capacity) {
if (*value && *value != ' ' && *value != '\t') return false;
const char* message;
if (get) {
message = *value ? "Error: usage get leds" : enabled() ? "> on" : "> off";
message = *value ? MESH_CLI_REPLY_TEXT("Error: usage get leds", "Error: get leds") : enabled() ? "> on" : "> off";
} else {
while (*value == ' ' || *value == '\t') ++value;
const bool on = strcmp(value, "on") == 0;
if (!on && strcmp(value, "off") != 0) {
message = "Error: usage set leds on|off";
message = MESH_CLI_REPLY_TEXT("Error: usage set leds on|off", "Error: set leds on|off");
} else if (!main_board || !saveSetting(on)) {
message = "Error: LED setting save unconfirmed; reboot may differ";
} else {
+6 -4
View File
@@ -1,5 +1,7 @@
#pragma once
#include "CLIReplyText.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@@ -315,17 +317,17 @@ inline bool handleCommand(const char* command, char* reply, size_t capacity,
if (argument(tail, "on") || argument(tail, "off")) {
const bool on = argument(tail, "on");
setEnabled(on, now_ms);
snprintf(reply, capacity, "OK - power diagnostics %s (RAM only; sleep policy unchanged)",
snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("OK - power diagnostics %s (RAM only; sleep policy unchanged)", "OK - power diag %s (RAM only; sleep policy unchanged)"),
on ? "on" : "off");
} else snprintf(reply, capacity, "Error: use set powersaving.diag on|off");
} else snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("Error: use set powersaving.diag on|off", "Error: set powersaving.diag on|off"));
return true;
}
tail = suffix(command, "clear powersaving.diag");
if (tail) {
if (!*tail) {
reset(now_ms);
snprintf(reply, capacity, "OK - power diagnostics cleared; sleep policy unchanged");
} else snprintf(reply, capacity, "Error: use clear powersaving.diag");
snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("OK - power diagnostics cleared; sleep policy unchanged", "OK - power diag cleared; sleep policy unchanged"));
} else snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("Error: use clear powersaving.diag", "Error: clear powersaving.diag"));
return true;
}
tail = suffix(command, "get powersaving.diag");
+12 -11
View File
@@ -1,3 +1,4 @@
#include "CLIReplyText.h"
#include "RadioProfileCLI.h"
#include "CarrierTools.h"
#if MESH_ENABLE_CARRIER_TOOLS
@@ -1144,7 +1145,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
reply_setting_ = 0xb0 | policy | (force ? 8 : 0);
if (!save(saved_, primary_preamble_, cross_)) {
reply_setting_ = previous;
cli::formatError(reply, capacity, "settings could not be saved");
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("settings could not be saved", "settings not saved"));
} else {
publish();
snprintf(reply, capacity, "OK - tx.reply=%s%s", radioTxPolicyName(policy), force ? " force" : "");
@@ -1182,7 +1183,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
} else if (verb == Set && (!strcmp(args, "auto") || !strcmp(args, "on") || !strcmp(args, "off"))) {
auto mode = !strcmp(args, "auto") ? RadioCrossMode::Auto : !strcmp(args, "on") ? RadioCrossMode::On : RadioCrossMode::Off;
if (save(saved_, primary_preamble_, mode)) { cross_ = mode; publish(); snprintf(reply, capacity, "OK"); }
else cli::formatError(reply, capacity, "settings could not be saved");
else cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("settings could not be saved", "settings not saved"));
} else cli::formatError(reply, capacity, "use get/set radio2.cross [auto|on|off]");
return true;
}
@@ -1249,7 +1250,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
return true;
}
if (!save({}, primary_preamble_, cross_, 0)) {
cli::formatError(reply, capacity, "settings could not be saved"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("settings could not be saved", "settings not saved")); return true;
}
noteSecondaryManualChange();
saved_ = {}; manual_secondary_ = false;
@@ -1264,7 +1265,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
if ((base || temporary) && verb == Set && !strcmp(args, "off")) {
if (base && !(remote_command_ ? prepareSavedImage(ReplyPath, {}, primary_preamble_, cross_, true)
: save({}, primary_preamble_, cross_, true))) {
cli::formatError(reply, capacity, "settings could not be saved"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("settings could not be saved", "settings not saved")); return true;
}
if (remote_command_) {
stageRemoteMutation(base ? RemoteMutation::Off : RemoteMutation::TempOff);
@@ -1287,7 +1288,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
if (scheduled && (verb == Get || verb == Delete)) {
uint32_t index = 0;
if (*args && strcmp(args, "all") && (!cli::parseUnsignedIntegerStrict(args, index) || index < 1 || index > 4)) {
cli::formatError(reply, capacity, "slot must be 1-4 or all"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("slot must be 1-4 or all", "slot: 1-4|all")); return true;
}
const unsigned first = scheduled_temp ? 4 : 0;
if (verb == Delete) {
@@ -1326,7 +1327,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
}
return true;
}
if (verb != Set) { cli::formatError(reply, capacity, "invalid radio2 command"); return true; }
if (verb != Set) { cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("invalid radio2 command", "bad radio2 command")); return true; }
if (base) {
const char* preset = cli::radio2PresetTuple(args);
if (preset) args = preset;
@@ -1354,15 +1355,15 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
if (valid) valid = radio_->validateProfile(config.params);
RadioProfiles preview = *radio_->profiles(); preview.setSecondary(config, false);
if (valid) valid = preview.automaticPreambleFits();
if (!valid) { snprintf(reply, capacity, "Error: use %s f,bw,sf,cr,rx|rxtx%s[,preamble|auto]", key,
if (!valid) { snprintf(reply, capacity, MESH_CLI_REPLY_TEXT("Error: use %s f,bw,sf,cr,rx|rxtx%s[,preamble|auto]", "Error: %s f,bw,sf,cr,rx|rxtx%s[,preamble|auto]"), key,
scheduled_temp ? ",start,end (UTC or +minutes)" : scheduled ? ",start (UTC or +minutes)" : temporary ? ",minutes" : ""); return true; }
if (base && !mediaMeshScopeReady(config)) {
cli::formatError(reply, capacity, "MediaMesh region unavailable or full"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("MediaMesh region unavailable or full", "MediaMesh region unavailable/full")); return true;
}
if (scheduled) {
if (!epoch || start <= epoch || (uint64_t)(start-epoch) * 1000 > 0x7fffffffULL
|| (scheduled_temp && (end <= start || (uint64_t)(end-epoch) * 1000 > 0x7fffffffULL))) {
cli::formatError(reply, capacity, "future UTC start/end required, within 24 days"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("future UTC start/end required, within 24 days", "future UTC start/end within 24 days")); return true;
}
if (scheduled_temp) {
const uint64_t pending_left = temp_pending_
@@ -1397,7 +1398,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
snprintf(reply, capacity, "OK - %s %u queued, preamble=%u", key, i+1, preview.preamble(1, 32));
appendSecondaryWarnings(reply, capacity, preview); return true;
}
cli::formatError(reply, capacity, "all four schedule slots occupied"); return true;
cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("all four schedule slots occupied", "all four slots full")); return true;
}
if (temporary) {
for (const auto& s : schedules_) if (s.active && s.temporary) {
@@ -1429,7 +1430,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
}
snprintf(reply, capacity, "OK - radio2 %s%s; preamble=%u", modeName(config.mode),
remote_command_ ? " pending after reply" : "", preview.preamble(1, 32));
} else cli::formatError(reply, capacity, "settings could not be saved");
} else cli::formatError(reply, capacity, MESH_CLI_REPLY_TEXT("settings could not be saved", "settings not saved"));
if (!strncmp(reply, "OK", 2)) appendSecondaryWarnings(reply, capacity, preview);
return true;
}
+2 -1
View File
@@ -1,3 +1,4 @@
#include "CLIReplyText.h"
#include "StorageLayout.h"
#if defined(ESP32_PLATFORM)
@@ -185,7 +186,7 @@ bool handleStorageLayoutGet(const char* config, MainBoard& board, char* reply,
if (match == StorageLayoutGetMatch::NotMatched) return false;
if (reply == nullptr || reply_size == 0) return true;
if (match == StorageLayoutGetMatch::InvalidArguments) {
snprintf(reply, reply_size, "Error: use get storage.layout");
snprintf(reply, reply_size, MESH_CLI_REPLY_TEXT("Error: use get storage.layout", "Error: get storage.layout"));
} else {
formatStorageLayout(board, reply, reply_size);
}
+5 -3
View File
@@ -1,5 +1,7 @@
#pragma once
#include "CLIReplyText.h"
#include <atomic>
#include <stdint.h>
#include <stdio.h>
@@ -141,7 +143,7 @@ public:
const uint8_t available = backend_ ? backend_->available() : 0;
const char* name = command.scope == All ? "2.4ghz" : command.scope == EspNow ? "espnow" : "wifi";
if (command.action == Action::Invalid) {
snprintf(reply, size, "Error: use set wifi/espnow on|off [force]; set 2.4ghz on [all|force]|off [force]");
snprintf(reply, size, MESH_CLI_REPLY_TEXT("Error: use set wifi/espnow on|off [force]; set 2.4ghz on [all|force]|off [force]", "Error: set wifi/espnow on|off [force]; set 2.4ghz on [all|force]|off [force]"));
return true;
}
const uint8_t current = enabled();
@@ -158,7 +160,7 @@ public:
}
#endif
if (!(available & command.scope)) {
snprintf(reply, size, "Error: requested wireless service unavailable in this build/boot");
snprintf(reply, size, MESH_CLI_REPLY_TEXT("Error: requested wireless service unavailable in this build/boot", "Error: wireless service unavailable this build/boot"));
return true;
}
const bool on = command.action == Action::On || command.action == Action::OnAll;
@@ -167,7 +169,7 @@ public:
return true;
}
if (command.scope != All && on && master_off_) {
snprintf(reply, size, "Error: 2.4ghz is off; use set 2.4ghz on first");
snprintf(reply, size, MESH_CLI_REPLY_TEXT("Error: 2.4ghz is off; use set 2.4ghz on first", "Error: 2.4ghz off; set 2.4ghz on first"));
return true;
}
const uint8_t scope = command.scope & available;
+598
View File
@@ -0,0 +1,598 @@
{
"schema": 1,
"profiles": [
"wio-e5-mini_repeater"
],
"messages": [
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, unsupported on this role",
"concise": "Error, unsupported role"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, retry configuration unsupported on this role",
"concise": "Error, retry unsupported for role"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "ERROR: dutycycle must be 1-100",
"concise": "ERROR: dutycycle 1-100"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, invalid airtime factor",
"concise": "Error, bad airtime factor"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "ERROR: telemetry.access must be all or acl",
"concise": "ERROR: telemetry.access all|acl"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error: interval must be 0 (off) or %d-240 minutes",
"concise": "Error: interval 0 (off) or %d-240 minutes"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0,1, or 2",
"concise": "Error, 0|1|2"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be on or off",
"concise": "Error, on|off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-20",
"concise": "Error, 0-20"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-2",
"concise": "Error, 0-2"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-40 dB",
"concise": "Error, 0-40 dB"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 1-15",
"concise": "Error, 1-15"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 10-5000 ms",
"concise": "Error, 10-5000 ms"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-5000 ms",
"concise": "Error, 0-5000 ms"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be all or 1-7",
"concise": "Error, all|1-7"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-15",
"concise": "Error, 0-15"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be 0-63 or off",
"concise": "Error, 0-63|off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be infra, rooftop, or mobile",
"concise": "Error, infra|rooftop|mobile"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use up to %u comma-separated 3-byte hex prefixes",
"concise": "Error, max %u comma-separated 3-byte hex prefixes"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use on or off on this role",
"concise": "Error, on|off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, SNR must fit -32.00..31.75 dB",
"concise": "Error, SNR -32.00..31.75 dB"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use CR4,CR5,CR7,CR8 SNRs or off",
"concise": "Error, CR4,CR5,CR7,CR8 SNRs|off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, must be: off, minimal, moderate, or strict",
"concise": "Error, off|minimal|moderate|strict"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error: TX power range is %d to %d dBm",
"concise": "Error: TX power %d..%d dBm"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, invalid radio params",
"concise": "Error, bad radio params"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, invalid latitude",
"concise": "Error, bad latitude"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, invalid longitude",
"concise": "Error, bad longitude"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, table rejected prefix",
"concise": "Error, prefix rejected"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Err - unknown or ambiguous jump: %s",
"concise": "Err - unknown/ambiguous jump: %s"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "ERROR: Power management not supported",
"concise": "ERROR: Power management unsupported"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - repeat is now OFF",
"concise": "OK - repeat OFF"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - repeat is now ON",
"concise": "OK - repeat ON"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - clock set: %02d:%02d - %d/%d/%d UTC",
"concise": "OK - clock: %02d:%02d - %d/%d/%d UTC"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - interval rounded to %d",
"concise": "OK - rounded interval %d"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "radio settings could not be saved",
"concise": "radio settings not saved"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "unsupported on this platform",
"concise": "platform unsupported"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - LoRa FEM RX gain on",
"concise": "OK - FEM RX gain on"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - LoRa FEM RX gain off",
"concise": "OK - FEM RX gain off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - LoRa FEM TX gain on",
"concise": "OK - FEM TX gain on"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "OK - LoRa FEM TX gain off",
"concise": "OK - FEM TX gain off"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "ERROR: range is %lu-%lu us",
"concise": "ERROR: range %lu-%lu us"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, argument too long",
"concise": "Error, arg too long"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, SNR must be numeric",
"concise": "Error, numeric SNR needed"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, invalid frequency",
"concise": "Error, bad frequency"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use: tempradio f,bw,sf,cr,minutes[,preamble]",
"concise": "Error, tempradio f,bw,sf,cr,minutes[,preamble]"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use: set radioat f,bw,sf,cr,start (UTC or +minutes)",
"concise": "Error, set radioat f,bw,sf,cr,start (UTC or +minutes)"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use: set tempradioat f,bw,sf,cr,start,end (UTC or +minutes)",
"concise": "Error, set tempradioat f,bw,sf,cr,start,end (UTC or +minutes)"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use: set recent.repeater <hex> [snr_db]",
"concise": "Error, set recent.repeater <hex> [snr_db]"
},
{
"file": "src/helpers/CommonCLI.cpp",
"verbose": "Error, use: get recent.repeaters [page] | search <2|4|6 hex> [page]",
"concise": "Error, get recent.repeaters [page] | search <2|4|6 hex> [page]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - blacklist slot must be 1-%u",
"concise": "Err - blacklist slot: 1-%u"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - expected six-digit repeater ID",
"concise": "Err - repeater ID: six hex digits"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - use 1-%d unique six-digit hex IDs separated by commas",
"concise": "Err - use 1-%d unique six-digit hex IDs, comma-separated"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - next blacklist slot is %u",
"concise": "Err - next blacklist slot: %u"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - blacklist range exceeds slot %u",
"concise": "Err - blacklist range past slot %u"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - unable to save flood filter blacklist",
"concise": "Err - flood filter blacklist not saved"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - filter slot must be 1-%d",
"concise": "Err - filter slot: 1-%d"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - packet type must be name, any, 0-15, or 0x00-0x0F",
"concise": "Err - type: name|any|0-15|0x00-0x0F"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - tx timing must be fast or slow",
"concise": "Err - tx: fast|slow"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - scope must be a public name of at most 30 characters",
"concise": "Err - scope: public name, max 30 chars"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - unable to save flood filter",
"concise": "Err - flood filter not saved"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - too many flood filter parameters",
"concise": "Err - excess flood filter parameters"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - require=region requires scope=<name>",
"concise": "Err - require=region needs scope=<name>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - tx timing requires scope=<name>",
"concise": "Err - tx needs scope=<name>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - scope slot must be 1-%d",
"concise": "Err - scope slot: 1-%d"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - path bucket must be 1-6",
"concise": "Err - path bucket: 1-6"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - require slot must be 1-%d",
"concise": "Err - require slot: 1-%d"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - channel must be public, #name, or 128/256-bit key",
"concise": "Err - channel: public|#name|128/256-bit key"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - permissions must be 0-255",
"concise": "Err - permissions: 0-255"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - region names cannot contain spaces",
"concise": "Err - region name: no spaces"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - unknown or invalid alert region",
"concise": "Err - unknown/invalid alert region"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - define a usable region before enabling battery alerts",
"concise": "Err - battery alerts need a usable region first"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - multiple narrowest regions; specify one",
"concise": "Err - specify one of the narrowest regions"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - low must be greater than critical",
"concise": "Err - low must exceed critical"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - critical must be less than low",
"concise": "Err - critical must be below low"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "OK - all flood filters removed",
"concise": "OK - flood filters cleared"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - use get guest or set guest on|off",
"concise": "Err - get guest / set guest on|off"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - region load owned by admin/region manager",
"concise": "Err - region load: admin/region manager only"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error: start is in the past",
"concise": "Error: start in past"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error: end is in the past",
"concise": "Error: end in past"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error: end must be after start",
"concise": "Error: end must follow start"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - another remote command is still running",
"concise": "Err - remote command busy"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - remote command is too long",
"concise": "Err - remote command too long"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - filter parameters too long",
"concise": "Err - filter params too long"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - use: set flood.filter[.n] <type> [hops] [path=blacklist] [scope=<name>] [require=region] [tx=slow] [suspend=tempradio]",
"concise": "Err - set flood.filter[.n] <type> [hops] [path=blacklist] [scope=<name>] [require=region] [tx=slow] [suspend=tempradio]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error, use: get tempradioat [n]",
"concise": "Error, get tempradioat [n]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error, use: get radioat [n]",
"concise": "Error, get radioat [n]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error, use: del tempradioat [n]",
"concise": "Error, del tempradioat [n]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Error, use: del radioat [n]",
"concise": "Error, del radioat [n]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - use: del flood.filter.blacklist[.<1-%u>| all]",
"concise": "Err - del flood.filter.blacklist[.<1-%u>| all]"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - use: del flood.filter.<1-%d>|all",
"concise": "Err - del flood.filter.<1-%d>|all"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - usage: send text.flood <message>",
"concise": "Err - send text.flood <message>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - usage: set rx.watchdog <on|off>",
"concise": "Err - set rx.watchdog <on|off>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - usage: set battery.alert <on [region]|off>",
"concise": "Err - set battery.alert <on [region]|off>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - usage: set battery.alert.low <1-100>",
"concise": "Err - set battery.alert.low <1-100>"
},
{
"file": "examples/simple_repeater/MyMesh.cpp",
"verbose": "Err - usage: set battery.alert.critical <0-99>",
"concise": "Err - set battery.alert.critical <0-99>"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "slot must be 1-4 or all",
"concise": "slot: 1-4|all"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "future UTC start/end required, within 24 days",
"concise": "future UTC start/end within 24 days"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "all four schedule slots occupied",
"concise": "all four slots full"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "settings could not be saved",
"concise": "settings not saved"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "invalid radio2 command",
"concise": "bad radio2 command"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "MediaMesh region unavailable or full",
"concise": "MediaMesh region unavailable/full"
},
{
"file": "src/helpers/RadioProfileCLI.cpp",
"verbose": "Error: use %s f,bw,sf,cr,rx|rxtx%s[,preamble|auto]",
"concise": "Error: %s f,bw,sf,cr,rx|rxtx%s[,preamble|auto]"
},
{
"file": "src/helpers/WirelessControl.h",
"verbose": "Error: requested wireless service unavailable in this build/boot",
"concise": "Error: wireless service unavailable this build/boot"
},
{
"file": "src/helpers/WirelessControl.h",
"verbose": "Error: 2.4ghz is off; use set 2.4ghz on first",
"concise": "Error: 2.4ghz off; set 2.4ghz on first"
},
{
"file": "src/helpers/WirelessControl.h",
"verbose": "Error: use set wifi/espnow on|off [force]; set 2.4ghz on [all|force]|off [force]",
"concise": "Error: set wifi/espnow on|off [force]; set 2.4ghz on [all|force]|off [force]"
},
{
"file": "src/helpers/PowerSavingDiagnostics.h",
"verbose": "OK - power diagnostics %s (RAM only; sleep policy unchanged)",
"concise": "OK - power diag %s (RAM only; sleep policy unchanged)"
},
{
"file": "src/helpers/PowerSavingDiagnostics.h",
"verbose": "OK - power diagnostics cleared; sleep policy unchanged",
"concise": "OK - power diag cleared; sleep policy unchanged"
},
{
"file": "src/helpers/PowerSavingDiagnostics.h",
"verbose": "Error: use set powersaving.diag on|off",
"concise": "Error: set powersaving.diag on|off"
},
{
"file": "src/helpers/PowerSavingDiagnostics.h",
"verbose": "Error: use clear powersaving.diag",
"concise": "Error: clear powersaving.diag"
},
{
"file": "src/helpers/FloodAdvertCLI.h",
"verbose": "ERR: advert limiter unavailable on this role",
"concise": "ERR: advert limiter unavailable for role"
},
{
"file": "src/helpers/FloodAdvertCLI.h",
"verbose": "ERR: advert list index out of range",
"concise": "ERR: advert index out of range"
},
{
"file": "src/helpers/FloodAdvertCLI.h",
"verbose": "OK - all advert limit history cleared",
"concise": "OK - all advert history cleared"
},
{
"file": "src/helpers/FloodAdvertCLI.h",
"verbose": "OK - key advert limit history cleared",
"concise": "OK - key advert history cleared"
},
{
"file": "src/helpers/LedSettings.cpp",
"verbose": "Error: usage get leds",
"concise": "Error: get leds"
},
{
"file": "src/helpers/LedSettings.cpp",
"verbose": "Error: usage set leds on|off",
"concise": "Error: set leds on|off"
},
{
"file": "src/helpers/StorageLayout.cpp",
"verbose": "Error: use get storage.layout",
"concise": "Error: get storage.layout"
}
],
"noncompact_tokens_sha256": {
"src/helpers/CommonCLI.cpp": "0a3d2e2eabd3e161e352176c4a6a4c4baa184944c08c93b522d4f1ddb5a24edd",
"examples/simple_repeater/MyMesh.cpp": "8567eeef2f327100e397c40b0aaf5b675fcdfea254987437d1ca8d005ad3a6f0",
"src/helpers/RadioProfileCLI.cpp": "d641d16e872eba3e73b9b5a50f9718c10b0b7da72782f793706ac36b3724a624",
"src/helpers/WirelessControl.h": "bbde183f091a8afa7fd65c7b379783f8be2ac4e74ae6037a99a71fde3fb89f1e",
"src/helpers/PowerSavingDiagnostics.h": "18b78f5780046ea8c772c4ce6c337741ae0c027039d9399c94c5f4205d114000",
"src/helpers/FloodAdvertCLI.h": "0f378e4d0f6c3b9b79991068a4b4208139484d2144ee1d7c5094d39043463be4",
"src/helpers/LedSettings.cpp": "df0681234ca1337148ad59caa935857bc692f98bf18603ca6d099c0382028400",
"src/helpers/StorageLayout.cpp": "5dd1c8bcbe2902e1f22d14f9ab425465bdc38926fae23989987d850b484cbb18"
}
}
+2 -2
View File
@@ -106,7 +106,7 @@ static void exercise(const std::string& kind, size_t size, bool advanced, bool a
const auto input = command(kind, args);
cli.run(input.c_str(), reply.data());
if (size >= COMMON_CLI_TMP_LEN) {
assert(strcmp(reply.data(), "Error, argument too long") == 0);
assert(strcmp(reply.data(), MESH_CLI_REPLY_TEXT("Error, argument too long", "Error, arg too long")) == 0);
untouched(cli);
for (char value : cli.tmp) assert(value == char(0x55));
} else if (kind == "sensor" || kind == "gps") {
@@ -117,7 +117,7 @@ static void exercise(const std::string& kind, size_t size, bool advanced, bool a
} else if (kind == "retry") {
assert(cli.callbacks.support_calls == 1);
assert(cli.saves == (advanced ? 1U : 0U));
assert(strcmp(reply.data(), advanced ? "OK" : "Error, use on or off on this role") == 0);
assert(strcmp(reply.data(), advanced ? "OK" : MESH_CLI_REPLY_TEXT("Error, use on or off on this role", "Error, on|off")) == 0);
if (advanced) {
assert(cli.prefs.direct_retry_cr_enabled == 1);
assert(cli.prefs.direct_retry_cr4_snr_x4 == 0 && cli.prefs.direct_retry_cr5_snr_x4 == 0);
+110
View File
@@ -0,0 +1,110 @@
"""Keep constrained CLI prose separate from commands and protocol replies."""
from pathlib import Path
import configparser
import hashlib
import json
import os
import re
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
FIXTURE = ROOT / 'test/fixtures/cli_reply_text.json'
SELECTION = re.compile(
r'MESH_CLI_REPLY_TEXT\(("(?:\\.|[^"\\])*"),\s*("(?:\\.|[^"\\])*")\)')
TOKENS = re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|'
r'//[^\n]*|/\*.*?\*/|[A-Za-z_][A-Za-z_0-9]*|[^\s]', re.S)
def original_tokens(source):
source = re.sub(r'^#include [<"](?:helpers/)?CLIReplyText\.h[>"]\s*\n',
'', source, flags=re.M)
source = SELECTION.sub(lambda match: match.group(1), source)
return '\n'.join(token.group() for token in TOKENS.finditer(source)
if not token.group().startswith(('//', '/*'))).encode()
class CliReplyTextTests(unittest.TestCase):
def test_only_reviewed_human_literals_change_and_control_flow_stays_exact(self):
fixture = json.loads(FIXTURE.read_text())
self.assertEqual(fixture['schema'], 1)
expected = {}
for row in fixture['messages']:
expected.setdefault(row['file'], set()).add((row['verbose'], row['concise']))
old, new = row['verbose'], row['concise']
self.assertLess(len(new), len(old))
formats = r'%(?:[-+ #0]*[0-9.*]*[a-zA-Z]+|%)'
self.assertEqual(re.findall(formats, old), re.findall(formats, new))
category = r'^(Error|ERROR|ERR|Err|OK)(?=[: ,\-])'
self.assertEqual(re.findall(category, old), re.findall(category, new))
self.assertEqual(re.findall(r'\d+(?:\.\d+)?', old),
re.findall(r'\d+(?:\.\d+)?', new))
self.assertFalse(old.startswith(('> ', '{', 'KEYBACKUP ', 'ACK')))
self.assertNotRegex(old.lower(),
r'host|replay|password|login|save unconfirmed|reboot|'
r'retained|default|warning|applying in|restore scheduled')
if 'unchanged' in old:
self.assertIn('sleep policy unchanged', old)
self.assertIn('sleep policy unchanged', new)
for file, messages in expected.items():
source = (ROOT / file).read_text()
actual = {(json.loads(match.group(1)), json.loads(match.group(2)))
for match in SELECTION.finditer(source)}
self.assertEqual(actual, messages, file)
# Substituting each original literal recovers every original C++
# token, including command tests, branches, calls and arguments.
self.assertEqual(hashlib.sha256(original_tokens(source)).hexdigest(),
fixture['noncompact_tokens_sha256'][file], file)
def test_profile_activation_is_explicit_and_default_is_off(self):
fixture = json.loads(FIXTURE.read_text())
enabled = []
for path in [ROOT / 'platformio.ini', *sorted((ROOT / 'variants').rglob('*.ini'))]:
config = configparser.ConfigParser(interpolation=None, strict=False)
config.read(path)
for section in config.sections():
if re.search(r'-D\s*MESH_CLI_COMPACT_REPLIES=1\b',
config[section].get('build_flags', '')):
self.assertTrue(section.startswith('env:'), str(path))
enabled.append(section[4:])
self.assertEqual(sorted(enabled), sorted(fixture['profiles']))
header = (ROOT / 'src/helpers/CLIReplyText.h').read_text()
self.assertIn('#ifndef MESH_CLI_COMPACT_REPLIES\n'
'#define MESH_CLI_COMPACT_REPLIES 0', header)
def test_actual_header_selects_exact_text_without_a_runtime_lookup(self):
compiler = os.environ.get('CXX') or shutil.which('g++') or shutil.which('clang++')
if not compiler:
self.skipTest('host C++ compiler required')
messages = json.loads(FIXTURE.read_text())['messages']
selections = ',\n'.join('MESH_CLI_REPLY_TEXT(%s, %s)' %
(json.dumps(row['verbose']), json.dumps(row['concise']))
for row in messages)
source = ('#include <CLIReplyText.h>\n#include <cstdio>\n'
'int main(){const char* text[]={'+selections+'};'
'for(const char* line:text)puts(line);}\n')
with tempfile.TemporaryDirectory(prefix='cli-reply-text-') as folder:
work = Path(folder)
(work / 'test.cpp').write_text(source)
for flag, key in ((None, 'verbose'), (0, 'verbose'), (1, 'concise')):
flags = [] if flag is None else ['-DMESH_CLI_COMPACT_REPLIES='+str(flag)]
built = subprocess.run([compiler, '-std=c++11', '-Os', '-Wall', '-Wextra',
'-Werror', *flags, '-I'+str(ROOT / 'src/helpers'),
str(work / 'test.cpp'), '-o', str(work / 'test')], capture_output=True)
self.assertEqual(built.returncode, 0, built.stderr.decode(errors='replace'))
result = subprocess.run([str(work / 'test')], capture_output=True)
self.assertEqual(result.returncode, 0, result.stderr.decode(errors='replace'))
self.assertEqual(result.stdout.decode().splitlines(),
[row[key] for row in messages])
if flag == 1:
image = (work / 'test').read_bytes()
selected = {row['concise'] for row in messages}
for row in messages:
if row['verbose'] not in selected:
self.assertNotIn(row['verbose'].encode()+b'\0', image)
if __name__ == '__main__':
unittest.main()
+5 -2
View File
@@ -54,12 +54,15 @@ class CommonCLIArgumentBoundsTests(unittest.TestCase):
cls.work = tempfile.TemporaryDirectory(prefix="meshcore-cli-argument-bounds-")
cls.binaries = {}
for name, defines in (("default", []), ("larger", ["-DCOMMON_CLI_TMP_LEN=192"]),
("compact", ["-DMESH_CLI_COMPACT_REPLIES=1"]),
("legacy", [])):
generated = source
if name == "legacy":
# Restore all three audited unchecked copies as a negative control.
error = (r'(?:"Error, argument too long"|MESH_CLI_REPLY_TEXT\('
r'"Error, argument too long", "Error, arg too long"\))')
pattern = (r'if \(!copyCLIArgument\(tmp, sizeof\(tmp\), (&(?:command|config)\[\d+\])\)\) '
r'\{\s*strcpy\(reply, "Error, argument too long"\);\s*return;\s*\}')
r'\{\s*strcpy\(reply, ' + error + r'\);\s*return;\s*\}')
generated, count = re.subn(pattern, r"strcpy(tmp, \1);", generated)
assert count == 3, "review the negative controls after branch changes"
directory = Path(cls.work.name)
@@ -86,7 +89,7 @@ class CommonCLIArgumentBoundsTests(unittest.TestCase):
env=dict(os.environ, ASAN_OPTIONS="detect_leaks=0:abort_on_error=0"))
def test_production_boundaries_and_rejection_without_effects(self):
for variant in ("default", "larger"):
for variant in ("default", "larger", "compact"):
with self.subTest(variant=variant):
result = self.run_case(variant, "production")
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
+1
View File
@@ -60,6 +60,7 @@ build_flags = ${lora_e5_mini.build_flags}
-D MESH_ENABLE_CLOCK_SYNC=0 ; preserve the 16 KiB filesystem on this 256 KiB part
-D MESH_ENABLE_FLOOD_GROUP_MODERATION=0 ; keep core routing and radio scheduling in flash
-D MESH_PACKET_LOGGING_COMPACT=1 ; retain complete RX/TX bytes without the duplicate parsed RX line
-D MESH_CLI_COMPACT_REPLIES=1 ; concise human prose; command/protocol replies stay intact
; User-approved Mini Repeater cuts only; other boards keep their defaults.
-D MESH_ENABLE_CARRIER_TOOLS=0
-D MESH_ENABLE_MANAGEMENT_REPORTING=0