Skip to content

Commit b66d607

Browse files
eriedclaude
andcommitted
Sources sheet: fix NPE on open — second init block runs after snapshots is built
The single init block was launching a `snapshots.collect { ... }` to fill the time-series buffers, but the `snapshots` val is declared AFTER the init block. Kotlin runs init blocks interleaved with property initialisers in declaration order, so at the point the init body ran, `snapshots` was still null — every open of the sheet crashed with NPE inside StateFlow.collect. Split into two init blocks: the first handles the IMU/RaceBox trail launches (no `snapshots` dependency); the second sits below the snapshots val declaration and handles the series append loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 8c0e870 commit b66d607

1 file changed

Lines changed: 22 additions & 12 deletions

File tree

app/src/main/java/com/eried/eucplanet/ui/dashboard/sources/DataSourcesViewModel.kt

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,18 +109,10 @@ class DataSourcesViewModel @Inject constructor(
109109
_raceboxTrail.update { it.takeLast(TRAIL_MAX - 1) + Offset(ax, az) }
110110
}
111111
}
112-
// Append to the time-series buffers from each snapshot tick.
113-
viewModelScope.launch {
114-
snapshots.collect { map ->
115-
val now = System.currentTimeMillis()
116-
map.forEach { (src, snap) ->
117-
appendSeries(_speedSeries[src], now, snap.speedKmh)
118-
appendSeries(_gMagnitudeSeries[src], now, snap.horizGMagnitude)
119-
appendSeries(_headingSeries[src], now, snap.headingDeg)
120-
appendSeries(_vertSpeedSeries[src], now, snap.verticalSpeedMps)
121-
}
122-
}
123-
}
112+
// The time-series append launch is in a second init block below,
113+
// AFTER snapshots is declared — referencing it here would NPE
114+
// because init blocks run interleaved with property initialisers
115+
// in declaration order.
124116
}
125117

126118
private fun appendSeries(flow: MutableStateFlow<TimedSeries>?, now: Long, v: Float?) {
@@ -191,6 +183,24 @@ class DataSourcesViewModel @Inject constructor(
191183
)
192184
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
193185

186+
init {
187+
// Second init block — runs AFTER `snapshots` is initialised above.
188+
// Each emit appends to the per-source per-metric rolling buffers
189+
// used by the Compare-tab line charts. Null fields are skipped
190+
// inside [appendSeries] so we don't taint the series with NaN.
191+
viewModelScope.launch {
192+
snapshots.collect { map ->
193+
val now = System.currentTimeMillis()
194+
map.forEach { (src, snap) ->
195+
appendSeries(_speedSeries[src], now, snap.speedKmh)
196+
appendSeries(_gMagnitudeSeries[src], now, snap.horizGMagnitude)
197+
appendSeries(_headingSeries[src], now, snap.headingDeg)
198+
appendSeries(_vertSpeedSeries[src], now, snap.verticalSpeedMps)
199+
}
200+
}
201+
}
202+
}
203+
194204
fun onSheetOpened() {
195205
phoneSensors.start()
196206
// Drop any stale trail from a previous opening so the visualisation

0 commit comments

Comments
 (0)