Skip to content

feat(discovery): mesh network discovery#5275

Draft
jamesarich wants to merge 27 commits into
mainfrom
feat/discovery
Draft

feat(discovery): mesh network discovery#5275
jamesarich wants to merge 27 commits into
mainfrom
feat/discovery

Conversation

@jamesarich
Copy link
Copy Markdown
Collaborator

@jamesarich jamesarich commented Apr 29, 2026

Add Local Mesh Discovery feature

  • Introduce a new :feature:discovery module for scanning mesh topology across multiple LoRa presets
  • Add DiscoveryScanEngine to manage scan lifecycles, preset shifting, and packet collection
  • Update database schema to version 39 with tables for discovery sessions, preset results, and discovered nodes
  • Implement UI screens for scan configuration, real-time progress, and historical session management
  • Add flavor-specific discovery maps (Google Maps and OSM) for visualizing node positions and topology
  • Include algorithmic and AI-powered summary generation for analyzing LoRa preset performance
  • Add report export functionality for Text and PDF formats
  • Integrate discovery entry point into the settings screen and navigation graphs

@github-actions github-actions Bot added the enhancement New feature or request label Apr 29, 2026
@jamesarich jamesarich marked this pull request as draft April 29, 2026 02:11
@jamesarich jamesarich changed the title feat(discovery): Local Mesh Discovery Improvements & Bug Fixes feat(discovery): mesh network discovery Apr 29, 2026
@jamesarich jamesarich requested a review from garthvh April 29, 2026 02:15
@jamesarich jamesarich force-pushed the feat/discovery branch 3 times, most recently from 891becc to 0335907 Compare April 30, 2026 13:19
@jamesarich jamesarich added this to the 2.8.0 milestone May 6, 2026
jamesarich and others added 19 commits May 20, 2026 17:08
* Introduce a new `:feature:discovery` module for scanning mesh topology across multiple LoRa presets
* Add `DiscoveryScanEngine` to manage scan lifecycles, preset shifting, and packet collection
* Update database schema to version 39 with tables for discovery sessions, preset results, and discovered nodes
* Implement UI screens for scan configuration, real-time progress, and historical session management
* Add flavor-specific discovery maps (Google Maps and OSM) for visualizing node positions and topology
* Include algorithmic and AI-powered summary generation for analyzing LoRa preset performance
* Add report export functionality for Text and PDF formats
* Integrate discovery entry point into the settings screen and navigation graphs
…tion restoration

- Implement distance tracking using `latLongToMeter` and a new `getMaxDistance` DAO query
- Calculate Airtime Rate as a delta over time to align with telemetry specifications
- Capture and restore the full `LoRaConfig` instead of just the modem preset after a scan
- Persist local radio statistics (Tx/Rx counts, uptime, relay stats) in preset results
- Backfill missing node names and positions from the local NodeDB during discovery
- Refactor `DiscoveryScanEngine` to use injected `CoroutineDispatchers` and `ApplicationCoroutineScope`
- Reduce BLE connection priority request delay in `BleRadioTransport` to 1 second
- Improve test reliability by replacing fixed `Thread.sleep` calls with state-based polling and `delay`
- Replace platform-specific `String.format` with Kotlin standard library `padStart` in `DwellProgressIndicator` to support common code.
- Simplify fully qualified icon references in `DiscoveryGoogleMap` by adding explicit imports.
…ests

- Add Preparing, Cancelling, Failed states to DiscoveryScanState (FR-008)
- Change Complete to data class with CompletionOutcome enum
- Add local-mesh-discovery deep link routes to DeepLinkRouter (FR-031)
- Compute packetSuccessRate/packetFailureRate in scan engine (FR-012)
- Fix DiscoveryScanEngineTest compilation and restructure with shared scheduler
- All 8 tests pass, kmpSmokeCompile clean
…c heuristic (D029)

Implement the spec's ranking and recommendation heuristic:
1. Highest unique discovered node count
2. Highest neighbor-report diversity (direct + mesh)
3. Highest non-duplicate packet count
4. Best median link quality (SNR first, then RSSI)
5. Greatest best known distance
6. Lowest failure/reconnect penalty
Presets tied after all 6 criteria share the same rank with isTied=true.
Includes RankingScoreBreakdown for transparent per-criterion scoring.
11 unit tests covering each criterion as tiebreaker, full ties,
edge cases (empty/single preset, no nodes, failed presets).
Validated: spotlessApply, allTests, kmpSmokeCompile
Mark 30 of 49 tasks as complete based on code review of the
feat/discovery branch. Key phases completed: setup (P1), data model
(P2 partial), scan engine (P3), packet collection (P4 partial),
map (P5 partial), summary (P6 partial), AI (P7 partial), history (P8).
Remaining: D010-D011 (DAO/migration tests), D012 (prefs), D020
(neighbor info trigger), D023-D024 (tests + map filter), D028
(map UI tests), D030/D032 (summary models + tests), D037 (AI tests),
D042 (history tests), D044-D045/D047-D048 (polish).
- Add rankings StateFlow to DiscoverySummaryViewModel
- Compute rankings from PresetRankingInput on loadNodes and rerunAnalysis
- Pass rank/isTied to PresetResultCard for display
- Show rank badge (#1, #2, tied) in preset header
- Rank 1 (untied) highlighted in primary color
Validated: spotlessApply, allTests, compileKotlinJvm
Add DataStore-backed preferences for discovery scan settings:
- dwellMinutes: persisted dwell time per preset (default 15)
- selectedPresets: last-used preset selection (restored on reopen)
- aiEnabled: whether AI summary expansion is enabled
- topologyOverlayEnabled: map topology overlay default
Architecture:
- Interface: DiscoveryPrefs in core:repository/AppPreferences.kt
- Implementation: DiscoveryPrefsImpl in core:prefs (reuses UiDataStore)
- Fake: FakeDiscoveryPrefs in core:testing
- Wiring: DiscoveryViewModel restores prefs on init, persists on change
Validated: spotlessApply, allTests, compileKotlinJvm
…odules (D048)

- Fix copyright headers (2025-2026 → 2026) in ~30 files across feature/discovery, core/database, core/repository, core/data, core/prefs
- Fix detekt compose issues: add modifier params to all public composable functions, fix parameter ordering in PresetResultCard, rename onMinutesSelected → onMinuteSelect (past tense), suppress LongMethod for DiscoverySummaryContent
- Fix detekt suppressions: CompositionLocalAllowlist in LocalDiscoveryMapProvider, MagicNumber/ReturnCount in DeepLinkRouter.routeSettings
- Mark D048 complete in tasks.md
All verification passes: spotlessCheck ✓, detekt ✓, tests ✓, kmpSmokeCompile ✓
- Added semantics annotations for screen readers
- Added progress announcements for dwell indicator
- Added disabled-preset explanations
- Migrated string resources to strings.xml
- Added DiscoveryMigrationTest for string resource migration
- Marked D000, D011, D044 as complete in tasks.md
…sources in settings

- Fix resource leak: collectorRegistry.collector was never cleared in
  the successful scan completion path (only in stop/abort paths)
- Replace hardcoded 'Local Mesh Discovery' strings in both Settings
  screens with stringResource(Res.string.discovery_local_mesh)
- Add missing explicit imports for the CMP string accessor
- Remove redundant .gitkeep files from directories with content

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jamesarich and others added 7 commits May 20, 2026 17:09
Replace all hardcoded strings in discovery UI screens and components
with stringResource() calls using centralized string resources.

Files updated:
- DiscoverySummaryScreen: session stats, AI analysis labels
- DiscoveryHistoryDetailScreen: history metadata labels
- DiscoveryScanScreen: disabled button semantics descriptions
- PresetPickerCard: selected/unselected state descriptions
- PresetResultCard: scan result stat labels
- RfHealthSection: RF health stat labels
- DwellProgressIndicator: dwell progress text

Added 33 new discovery_stat_* and discovery_* string resources.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Update status from 'Not Started' to 'Implementation Complete'
- Add Implementation Status section documenting per-user-story completion
- Add data model divergences (simplified schema, RF health fields, neighborType)
- Add Cross-Platform Alignment section comparing with meshtastic-apple
- Document intentional differences (2-level state machine, production nav location)
- Document known divergences and their priority (radar sweep, icon classification)
- Reference design repo audit confirmation (50/51 tasks, D048 remaining)
- Add implementation note to data-model.md about actual vs proposed schema

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the stub GeminiNanoSummaryProvider with a real implementation
that uses com.google.mlkit:genai-prompt:1.0.0-beta2 for on-device
AI-powered scan summaries on supported Android hardware.

Implementation:
- Generation.getClient() to obtain the GenerativeModel
- generateContentRequest with TextPart for structured prompts
- Temperature 0.3, topK 16, maxOutputTokens 200 for concise output
- Graceful fallback to DiscoverySummaryGenerator on any failure
- Lazy model initialization with error logging via Kermit

The existing buildSessionPrompt() and buildPresetPrompt() methods in
DiscoverySummaryGenerator provide the prompt text. On unsupported
devices or fdroid builds, the provider falls through to the
deterministic algorithmic summary seamlessly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add 31 tests covering:
- generateSessionSummary: empty presets, single/multi preset, ranking,
  congestion detection, traffic mix, completion status, recommendations
- generatePresetSummary: node counts, channel util, congestion marking,
  traffic dominance, known preset data rate inclusion
- buildSessionPrompt: instructions, session metadata, preset data,
  channel utilization, congestion guidance
- buildPresetPrompt: preset name, metrics, guidance context

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Use locale-aware DateFormatter.formatDateTimeShort() instead of
  hardcoded YYYY-MM-DD HH:mm format in history screen
- Filter deprecated presets (VERY_LONG_SLOW, LONG_SLOW) from picker
- Add minimum packet threshold (5) for traffic mix classification
  to avoid noise from trivial counts
- Add LITE_FAST, LITE_SLOW, NARROW_FAST, NARROW_SLOW entries to
  LoRaPresetReference for 2.4 GHz preset AI prompt enrichment
- Add lowTrafficCountsNoMixNote test case

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ssion recovery, default key guard

- Track infrastructure nodes (ROUTER, ROUTER_LATE, CLIENT_BASE roles)
  in DiscoveredNodeEntity and DiscoveryPresetResultEntity
- Add markInterruptedSessions() DAO query for cold-start recovery
- Add usesDefaultKey StateFlow to DiscoveryViewModel that checks
  primary channel PSK and disables scan when using default/cleartext key
- Wire default key guard into ScanButton with accessibility description
- Add discovery_start_scan_reason_default_key string resource
- Update all test DAO fakes with KMP-compatible implementations

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Critical spec gaps resolved:
- Wire Check24GhzCapability into DiscoveryViewModel; expose is24GhzBlocked
  and isLora24Region states; scan button disabled when on LORA_24 region
  with unsupported hardware
- Implement rememberExportSaver expect/actual composable:
  Android uses SAF ACTION_CREATE_DOCUMENT, Desktop uses JFileChooser,
  iOS stub logs warning. Summary screen now saves export result to disk.
- Add discovery_start_scan_reason_24ghz_unsupported string resource

Spec updates:
- Mark US5 (2.4 GHz gating) and Export as complete
- Document 8 features implemented beyond original spec
- Add remaining map UI gaps to Known Divergences table
- Update design repo status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

🖼️ Preview staleness check — advisory

This PR modifies UI composables but does not update any *Previews.kt files.

Previews power screenshot tests and in-app docs screenshots. Keeping them current ensures visual regression coverage stays accurate.

Changed UI files:

feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryHistoryDetailScreen.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryHistoryScreen.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryMapScreen.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoveryScanScreen.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/DiscoverySummaryScreen.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/DwellProgressIndicator.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/PresetPickerCard.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/PresetResultCard.kt
feature/discovery/src/commonMain/kotlin/org/meshtastic/feature/discovery/ui/component/RfHealthSection.kt

What to check:

Pattern Preview file convention
feature/{name}/…/ui/ or component/ feature/{name}/…/*Previews.kt
core/ui/…/ core/ui/…/ (previews colocated)

Adding previews checklist:

  1. Create or update a *Previews.kt file in the same module with @PreviewLightDark
  2. Add @Suppress("PreviewPublic") if the preview is consumed by screenshot-tests
  3. Add corresponding @PreviewTest function in screenshot-tests/src/screenshotTest/
  4. Run ./gradlew :screenshot-tests:updateDebugScreenshotTest to generate reference images

If this PR does not require preview updates (e.g., logic-only change, non-visual refactor), add the skip-preview-check label to dismiss.

…t-rebase

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant