diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 821a21a2..85ca762e 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -5839,6 +5839,20 @@ class MeshCoreConnector extends ChangeNotifier { Duration timeout = const Duration(seconds: 10), }) async { _guardRegionScope(); + // One auto-retry: a single anon-request round-trip can drop over LoRa and a + // retry usually gets through; a genuinely unreachable repeater still ends in + // a timeout after both attempts (#836, D1 Option A). + for (var attempt = 0; attempt < 2; attempt++) { + final reply = await _discoverRegionsOnce(repeaterPubKey, timeout); + if (reply != null) return reply; + } + return null; + } + + Future _discoverRegionsOnce( + Uint8List repeaterPubKey, + Duration timeout, + ) async { // Reply travels back along the repeater's own stored path (firmware routes // it as-is), so a multi-hop repeater can answer. Empty => zero-hop. final (replyPathLenByte, replyPath) = _regionReplyPathFor(repeaterPubKey); diff --git a/test/connector/region_connector_test.dart b/test/connector/region_connector_test.dart index 0038248e..9480d43f 100644 --- a/test/connector/region_connector_test.dart +++ b/test/connector/region_connector_test.dart @@ -146,6 +146,30 @@ void main() { expect(sent![34], 2); // packed path-len byte: 2 hops, 1-byte hashes expect(sent!.sublist(35), [0xAA, 0xBB]); }); + + test( + 'auto-retries once: first attempt times out, second succeeds', + () async { + var sends = 0; + connector.sendFrameOverrideForTest = (_) { + sends++; + // First send injects nothing (that attempt times out); second answers. + if (sends >= 2) { + connector.handleFrameForTest(_respSent(0x77)); + connector.handleFrameForTest( + _regionsReply(0x77, clock: 1, csv: 'oki'), + ); + } + }; + final reply = await connector.discoverRegions( + repeaterPubKey: pub, + timeout: const Duration(milliseconds: 60), + ); + expect(reply, isNotNull); + expect(reply!.regionNames, ['oki']); + expect(sends, 2); + }, + ); }); group('send-scope setters', () {