Omit unused forwarding region automation from Full Companions
This commit is contained in:
1 parent
cf0628d3db
commit
7321f858d8
3 files changed
+73
-5
No files matched your search
@@ -92,14 +92,17 @@ bool secondaryOverlaps(uint32_t primary_hz, float secondary_mhz, uint32_t cleara
|
||||
return (primary_hz > secondary_hz ? primary_hz - secondary_hz : secondary_hz - primary_hz)
|
||||
<= clearance_hz;
|
||||
}
|
||||
#if !defined(COMPANION_RADIO_FULL)
|
||||
bool isMediaMeshProfile(const RadioProfileConfig& config) {
|
||||
return config.mode != RadioProfileMode::Off
|
||||
&& config.params.freq >= 911.1495f && config.params.freq <= 911.1505f
|
||||
&& (config.params.bw == 250.0f || config.params.bw == 125.0f)
|
||||
&& config.params.sf == 7 && config.params.cr == 5;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(COMPANION_RADIO_FULL)
|
||||
void RadioProfileCLI::setMediaMeshScopeHandler(MediaMeshScopeHandler handler, void* context) {
|
||||
media_scope_handler_ = handler;
|
||||
media_scope_context_ = context;
|
||||
@@ -129,6 +132,7 @@ void RadioProfileCLI::retryMediaMeshScope() {
|
||||
media_scope_pending_ = !media_scope_handler_(media_scope_context_, media_scope_enabled_, true);
|
||||
media_scope_retry_ms_ = media_scope_pending_ ? millis() + 1000 : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
RadioProfileCLI::ImageReadResult RadioProfileCLI::readImage(
|
||||
const char* path, uint8_t* bytes, size_t size) {
|
||||
@@ -934,7 +938,7 @@ void RadioProfileCLI::appendPrimaryChirpWarning(char* reply, size_t capacity, ui
|
||||
void RadioProfileCLI::appendSecondaryWarnings(char* reply, size_t capacity,
|
||||
const RadioProfiles& preview) const {
|
||||
if (!capacity) return;
|
||||
if (media_scope_pending_) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (mediaMeshScopePending()) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (preview.enabled() && RadioProfiles::valid(preview.primary)
|
||||
&& RadioProfiles::valid(preview.secondary.params)) {
|
||||
const uint32_t primary_hz = frequencyHz(preview.primary.freq);
|
||||
@@ -1188,7 +1192,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
|
||||
p.recommendationReady() ? " ready" : " pending", (unsigned long)p.switchBudgetUs());
|
||||
appendChirpWarning(reply, capacity, p);
|
||||
}
|
||||
if (media_scope_pending_) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (mediaMeshScopePending()) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
return true;
|
||||
}
|
||||
if ((base || temporary) && verb == Get && !*args) {
|
||||
@@ -1199,7 +1203,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
|
||||
}
|
||||
if (base && saved_.mode == RadioProfileMode::Off) {
|
||||
snprintf(reply, capacity, "> off (%s)", manual_secondary_ ? "manual" : "automatic allowed");
|
||||
if (media_scope_pending_) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (mediaMeshScopePending()) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -1227,7 +1231,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
|
||||
publish_pending_ = true; publish_after_ms_ = millis() + 2000;
|
||||
publish();
|
||||
snprintf(reply, capacity, "OK - automatic radio2 permitted in 2s");
|
||||
if (media_scope_pending_) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (mediaMeshScopePending()) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
@@ -1251,7 +1255,7 @@ bool RadioProfileCLI::handle(const char* command, char* reply, size_t capacity,
|
||||
for (auto& s : schedules_) if (base || s.temporary) s = {};
|
||||
publish_pending_ = true; publish_after_ms_ = millis() + 2000;
|
||||
publish(); snprintf(reply, capacity, "OK - %s in 2s", saved_.mode != RadioProfileMode::Off ? "saved radio2 restored" : "single radio");
|
||||
if (media_scope_pending_) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
if (mediaMeshScopePending()) appendReply(reply, capacity, "; MediaMesh scope save pending");
|
||||
return true;
|
||||
}
|
||||
if (scheduled && (verb == Get || verb == Delete)) {
|
||||
|
||||
@@ -61,6 +61,7 @@ class RadioProfileCLI {
|
||||
// readable backup. Read/I/O failures and an intact backup remain read-only.
|
||||
bool recoverable_corrupt_store_ = false;
|
||||
bool publish_pending_ = false;
|
||||
#if !defined(COMPANION_RADIO_FULL)
|
||||
MediaMeshScopeHandler media_scope_handler_ = nullptr;
|
||||
void* media_scope_context_ = nullptr;
|
||||
uint32_t media_scope_retry_ms_ = 0;
|
||||
@@ -68,6 +69,15 @@ class RadioProfileCLI {
|
||||
bool mediaMeshScopeReady(const RadioProfileConfig& config) const;
|
||||
void updateMediaMeshScope();
|
||||
void retryMediaMeshScope();
|
||||
bool mediaMeshScopePending() const { return media_scope_pending_; }
|
||||
#else
|
||||
// Full Companions have no forwarding RegionMap. Do not retain unreachable
|
||||
// infrastructure callbacks, state or failure text in their tight images.
|
||||
bool mediaMeshScopeReady(const RadioProfileConfig&) const { return true; }
|
||||
void updateMediaMeshScope() {}
|
||||
void retryMediaMeshScope() {}
|
||||
bool mediaMeshScopePending() const { return false; }
|
||||
#endif
|
||||
#if MESH_RADIO_SWITCH_TUNING
|
||||
RadioSwitchTuning fast_saved_;
|
||||
uint32_t fast_retry_ms_ = 0;
|
||||
@@ -131,7 +141,11 @@ class RadioProfileCLI {
|
||||
#endif
|
||||
void begin(FILESYSTEM* fs, Radio* radio, RTCClock* rtc, bool infrastructure_replies = false);
|
||||
// Bind only after RegionMap::load(), so boot reconciliation is not discarded.
|
||||
#if !defined(COMPANION_RADIO_FULL)
|
||||
void setMediaMeshScopeHandler(MediaMeshScopeHandler handler, void* context);
|
||||
#else
|
||||
void setMediaMeshScopeHandler(MediaMeshScopeHandler, void*) {}
|
||||
#endif
|
||||
void servicePendingTuning();
|
||||
bool hasPendingTuning() const {
|
||||
#if MESH_RADIO_SWITCH_TUNING
|
||||
|
||||
@@ -9,6 +9,56 @@ import unittest
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
class RadioProfileCLITest(unittest.TestCase):
|
||||
def test_full_companion_keeps_rf_presets_without_unused_region_code(self):
|
||||
compiler = shutil.which('g++') or shutil.which('clang++')
|
||||
self.assertIsNotNone(compiler)
|
||||
code = r'''
|
||||
#define main existing_cli_main
|
||||
#include "test/fixtures/radio_profiles/cli_test.cpp"
|
||||
#undef main
|
||||
int main() {
|
||||
Fixture f;
|
||||
MediaScope scope;
|
||||
f.cli.setMediaMeshScopeHandler(MediaScope::update, &scope);
|
||||
f.cmd("set radio2 mediamesh");
|
||||
f.advance(2000);
|
||||
assert(f.radio.p.enabled() && fabsf(f.radio.p.secondary.params.freq - 911.150f) < 0.0001f);
|
||||
#if defined(COMPANION_RADIO_FULL)
|
||||
assert(!scope.commits && !scope.checks);
|
||||
#else
|
||||
assert(scope.enabled && scope.durable && scope.commits);
|
||||
#endif
|
||||
f.cmd("set radio2 off");
|
||||
assert(!scope.enabled);
|
||||
printf("%zu", sizeof(mesh::RadioProfileCLI));
|
||||
}
|
||||
'''
|
||||
sizes = {}
|
||||
with tempfile.TemporaryDirectory(prefix='profile-region-role-') as work:
|
||||
source = Path(work)/'role.cpp'
|
||||
source.write_text(code, encoding='ascii')
|
||||
for full in (False, True):
|
||||
binary = Path(work)/('companion' if full else 'infrastructure')
|
||||
flags = ['-DCOMPANION_RADIO_FULL=1'] if full else []
|
||||
compiled = subprocess.run([
|
||||
compiler, '-std=c++17', '-Os', '-ffunction-sections',
|
||||
'-fdata-sections', '-Wl,--gc-sections',
|
||||
'-DMESH_ENABLE_FLEET_CONTROL=0', '-DMESH_ENABLE_CARRIER_TOOLS=0',
|
||||
'-DMESH_ENABLE_MESHTEXT_CHAT=0', '-DMESH_ENABLE_HOST_KISS=0',
|
||||
'-DMESH_ENABLE_ETHERNET_TUNNEL=0', *flags,
|
||||
'-I', str(ROOT), '-I', str(ROOT/'test/fixtures/radio_profiles/mocks'),
|
||||
'-I', str(ROOT/'test/mocks'), '-I', str(ROOT/'src'),
|
||||
str(ROOT/'src/helpers/RadioProfileCLI.cpp'), str(source),
|
||||
'-o', str(binary)], capture_output=True, text=True, timeout=60)
|
||||
self.assertEqual(compiled.returncode, 0, compiled.stderr)
|
||||
run = subprocess.run([str(binary)], capture_output=True, text=True, timeout=10)
|
||||
self.assertEqual(run.returncode, 0, run.stdout+run.stderr)
|
||||
sizes[full] = int(run.stdout)
|
||||
image = binary.read_bytes()
|
||||
for marker in (b'MediaMesh scope save pending', b'MediaMesh region unavailable or full'):
|
||||
self.assertEqual(marker in image, not full)
|
||||
self.assertLess(sizes[True], sizes[False])
|
||||
|
||||
def test_commands_persistence_and_timers(self):
|
||||
compiler = shutil.which('g++') or shutil.which('clang++')
|
||||
self.assertIsNotNone(compiler)
|
||||
|
||||
Reference in new issue
Block a user