Skip to content

Commit

Permalink
refactor: Conducting time filter at the component lvl to avoid metric…
Browse files Browse the repository at this point in the history
… nav cards from being disabled when we don't have recent data (#1402)
  • Loading branch information
Robert-0410 authored Nov 13, 2024
1 parent f6af9b8 commit 013e3de
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,9 @@ class MeshLogRepository @Inject constructor(private val meshLogDaoLazy: dagger.L
.toBuilder().setTime((log.received_date / MILLIS_TO_SECONDS).toInt()).build()
}.getOrNull()

/**
* @param timeFrame the oldest [Telemetry] to get.
*/
@OptIn(ExperimentalCoroutinesApi::class)
fun getTelemetryFrom(nodeNum: Int, timeFrame: Long): Flow<List<Telemetry>> =
meshLogDao.getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS, timeFrame)
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> =
meshLogDao.getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
.distinctUntilChanged()
.mapLatest { list -> list.mapNotNull(::parseTelemetryLog) }
.flowOn(Dispatchers.IO)
Expand All @@ -46,8 +43,7 @@ class MeshLogRepository @Inject constructor(private val meshLogDaoLazy: dagger.L
nodeNum: Int,
portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE,
maxItem: Int = MAX_MESH_PACKETS,
oldestTime: Long = 0L
): Flow<List<MeshLog>> = meshLogDao.getLogsFrom(nodeNum, portNum, maxItem, oldestTime)
): Flow<List<MeshLog>> = meshLogDao.getLogsFrom(nodeNum, portNum, maxItem)
.distinctUntilChanged()
.flowOn(Dispatchers.IO)

Expand All @@ -59,8 +55,7 @@ class MeshLogRepository @Inject constructor(private val meshLogDaoLazy: dagger.L
fun getMeshPacketsFrom(
nodeNum: Int,
portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE,
oldestTime: Long = 0L
): Flow<List<MeshPacket>> = getLogsFrom(nodeNum, portNum, oldestTime = oldestTime)
): Flow<List<MeshPacket>> = getLogsFrom(nodeNum, portNum)
.mapLatest { list -> list.map { it.fromRadio.packet } }
.flowOn(Dispatchers.IO)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ interface MeshLogDao {
* Retrieves [MeshLog]s matching 'from_num' (nodeNum) and 'port_num' (PortNum).
*
* @param portNum If 0, returns all MeshPackets. Otherwise, filters by 'port_num'.
* @param timeFrame oldest limit in milliseconds of [MeshLog]s we want to retrieve.
*/
@Query(
"""
SELECT * FROM log
WHERE from_num = :fromNum AND (:portNum = 0 AND port_num != 0 OR port_num = :portNum) AND received_date > :timeFrame
WHERE from_num = :fromNum AND (:portNum = 0 AND port_num != 0 OR port_num = :portNum)
ORDER BY received_date DESC LIMIT 0,:maxItem
"""
)
fun getLogsFrom(fromNum: Int, portNum: Int, maxItem: Int, timeFrame: Long = 0L): Flow<List<MeshLog>>
fun getLogsFrom(fromNum: Int, portNum: Int, maxItem: Int): Flow<List<MeshLog>>

@Insert
fun insert(log: MeshLog)
Expand Down
63 changes: 35 additions & 28 deletions app/src/main/java/com/geeksville/mesh/model/MetricsViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@ import com.geeksville.mesh.database.entity.MeshLog
import com.geeksville.mesh.repository.datastore.RadioConfigRepository
import com.geeksville.mesh.ui.Route
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
Expand All @@ -35,6 +33,7 @@ import java.io.FileNotFoundException
import java.io.FileWriter
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.concurrent.TimeUnit
import javax.inject.Inject

data class MetricsState(
Expand All @@ -54,6 +53,21 @@ data class MetricsState(
fun hasTracerouteLogs() = tracerouteRequests.isNotEmpty()
fun hasPositionLogs() = positionLogs.isNotEmpty()

fun deviceMetricsFiltered(timeFrame: TimeFrame): List<Telemetry> {
val oldestTime = timeFrame.calculateOldestTime()
return deviceMetrics.filter { it.time >= oldestTime }
}

fun environmentMetricsFiltered(timeFrame: TimeFrame): List<Telemetry> {
val oldestTime = timeFrame.calculateOldestTime()
return environmentMetrics.filter { it.time >= oldestTime }
}

fun signalMetricsFiltered(timeFrame: TimeFrame): List<MeshPacket> {
val oldestTime = timeFrame.calculateOldestTime()
return signalMetrics.filter { it.rxTime >= oldestTime }
}

companion object {
val Empty = MetricsState()
}
Expand All @@ -64,20 +78,20 @@ data class MetricsState(
*/
@Suppress("MagicNumber")
enum class TimeFrame(
val milliseconds: Long,
val seconds: Long,
@StringRes val strRes: Int
) {
TWENTY_FOUR_HOURS(86400000L, R.string.twenty_four_hours),
FORTY_EIGHT_HOURS(172800000L, R.string.forty_eight_hours),
ONE_WEEK(604800000L, R.string.one_week),
TWO_WEEKS(1209600000L, R.string.two_weeks),
ONE_MONTH(2629800000L, R.string.one_month),
TWENTY_FOUR_HOURS(TimeUnit.DAYS.toSeconds(1), R.string.twenty_four_hours),
FORTY_EIGHT_HOURS(TimeUnit.DAYS.toSeconds(2), R.string.forty_eight_hours),
ONE_WEEK(TimeUnit.DAYS.toSeconds(7), R.string.one_week),
TWO_WEEKS(TimeUnit.DAYS.toSeconds(14), R.string.two_weeks),
FOUR_WEEKS(TimeUnit.DAYS.toSeconds(28), R.string.four_weeks),
MAX(0L, R.string.max);

fun calculateOldestTime(): Long = if (this == MAX) {
MAX.milliseconds
MAX.seconds
} else {
System.currentTimeMillis() - this.milliseconds
System.currentTimeMillis() / 1000 - this.seconds
}
}

Expand Down Expand Up @@ -131,27 +145,20 @@ class MetricsViewModel @Inject constructor(
}
}.launchIn(viewModelScope)

@OptIn(ExperimentalCoroutinesApi::class)
_timeFrame.flatMapLatest { timeFrame ->
meshLogRepository.getTelemetryFrom(destNum, timeFrame.calculateOldestTime()).onEach { telemetry ->
_state.update { state ->
state.copy(
deviceMetrics = telemetry.filter { it.hasDeviceMetrics() },
environmentMetrics = telemetry.filter {
it.hasEnvironmentMetrics() && it.environmentMetrics.relativeHumidity >= 0f
},
)
}
meshLogRepository.getTelemetryFrom(destNum).onEach { telemetry ->
_state.update { state ->
state.copy(
deviceMetrics = telemetry.filter { it.hasDeviceMetrics() },
environmentMetrics = telemetry.filter {
it.hasEnvironmentMetrics() && it.environmentMetrics.relativeHumidity >= 0f
}
)
}
}.launchIn(viewModelScope)

@OptIn(ExperimentalCoroutinesApi::class)
_timeFrame.flatMapLatest { timeFrame ->
val oldestTime = timeFrame.calculateOldestTime()
meshLogRepository.getMeshPacketsFrom(destNum, oldestTime = oldestTime).onEach { meshPackets ->
_state.update { state ->
state.copy(signalMetrics = meshPackets.filter { it.hasValidSignal() })
}
meshLogRepository.getMeshPacketsFrom(destNum).onEach { meshPackets ->
_state.update { state ->
state.copy(signalMetrics = meshPackets.filter { it.hasValidSignal() })
}
}.launchIn(viewModelScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ fun DeviceMetricsScreen(
) {
val state by viewModel.state.collectAsStateWithLifecycle()
var displayInfoDialog by remember { mutableStateOf(false) }
val selectedTimeFrame by viewModel.timeFrame.collectAsState()
val data = state.deviceMetricsFiltered(selectedTimeFrame)

Column {

Expand All @@ -82,11 +84,10 @@ fun DeviceMetricsScreen(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(fraction = 0.33f),
state.deviceMetrics.reversed(),
data.reversed(),
promptInfoDialog = { displayInfoDialog = true }
)

val selectedTimeFrame by viewModel.timeFrame.collectAsState()
MetricsTimeSelector(
selectedTimeFrame,
onOptionSelected = { viewModel.setTimeFrame(it) }
Expand All @@ -98,7 +99,7 @@ fun DeviceMetricsScreen(
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(state.deviceMetrics) { telemetry -> DeviceMetricsCard(telemetry) }
items(data) { telemetry -> DeviceMetricsCard(telemetry) }
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ fun EnvironmentMetricsScreen(
viewModel: MetricsViewModel = hiltViewModel(),
) {
val state by viewModel.state.collectAsStateWithLifecycle()
val selectedTimeFrame by viewModel.timeFrame.collectAsState()
val data = state.environmentMetricsFiltered(selectedTimeFrame)

/* Convert Celsius to Fahrenheit */
@Suppress("MagicNumber")
Expand All @@ -85,7 +87,7 @@ fun EnvironmentMetricsScreen(
}

val processedTelemetries: List<Telemetry> = if (state.isFahrenheit) {
state.environmentMetrics.map { telemetry ->
data.map { telemetry ->
val temperatureFahrenheit =
celsiusToFahrenheit(telemetry.environmentMetrics.temperature)
telemetry.copy {
Expand All @@ -94,7 +96,7 @@ fun EnvironmentMetricsScreen(
}
}
} else {
state.environmentMetrics
data
}

var displayInfoDialog by remember { mutableStateOf(false) }
Expand All @@ -118,7 +120,6 @@ fun EnvironmentMetricsScreen(
promptInfoDialog = { displayInfoDialog = true }
)

val selectedTimeFrame by viewModel.timeFrame.collectAsState()
MetricsTimeSelector(
selectedTimeFrame,
onOptionSelected = { viewModel.setTimeFrame(it) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ fun SignalMetricsScreen(
) {
val state by viewModel.state.collectAsStateWithLifecycle()
var displayInfoDialog by remember { mutableStateOf(false) }
val selectedTimeFrame by viewModel.timeFrame.collectAsState()
val data = state.signalMetricsFiltered(selectedTimeFrame)

Column {

Expand All @@ -88,11 +90,10 @@ fun SignalMetricsScreen(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(fraction = 0.33f),
meshPackets = state.signalMetrics.reversed(),
meshPackets = data.reversed(),
promptInfoDialog = { displayInfoDialog = true }
)

val selectedTimeFrame by viewModel.timeFrame.collectAsState()
MetricsTimeSelector(
selectedTimeFrame,
onOptionSelected = { viewModel.setTimeFrame(it) }
Expand All @@ -103,7 +104,7 @@ fun SignalMetricsScreen(
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(state.signalMetrics) { meshPacket -> SignalMetricsCard(meshPacket) }
items(data) { meshPacket -> SignalMetricsCard(meshPacket) }
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@
<string name="forty_eight_hours">48H</string>
<string name="one_week">1W</string>
<string name="two_weeks">2W</string>
<string name="one_month">1M</string>
<string name="four_weeks">4W</string>
<string name="max">Max</string>
<string name="selected">Selected</string>
<string name="not_selected">Not Selected</string>
Expand Down

0 comments on commit 013e3de

Please sign in to comment.