mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor(graphs): Missing iaq (#1752)
This commit is contained in:
parent
64c13ac57d
commit
309ce27268
4 changed files with 230 additions and 171 deletions
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.geeksville.mesh.model
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.ui.theme.InfantryBlue
|
||||
import com.geeksville.mesh.ui.theme.Orange
|
||||
|
||||
enum class Environment(val color: Color) {
|
||||
TEMPERATURE(Color.Red) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.temperature
|
||||
}
|
||||
},
|
||||
HUMIDITY(InfantryBlue) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.relativeHumidity
|
||||
}
|
||||
},
|
||||
IAQ(Color.Green) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.iaq.toFloat()
|
||||
}
|
||||
},
|
||||
BAROMETRIC_PRESSURE(Orange) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.barometricPressure
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun getValue(telemetry: Telemetry): Float
|
||||
}
|
||||
|
||||
/**
|
||||
* @param metrics the filtered [List]
|
||||
* @param shouldPlot a [List] the size of [Environment] used to determine if a metric
|
||||
* should be plotted
|
||||
* @param leftMinMax [Pair] with the min and max of the barometric pressure
|
||||
* @param rightMinMax [Pair] with the combined min and max of: the temperature, humidity, and IAQ
|
||||
* @param times [Pair] with the oldest and newest times in that order
|
||||
*/
|
||||
data class EnvironmentGraphingData(
|
||||
val metrics: List<Telemetry>,
|
||||
val shouldPlot: List<Boolean>,
|
||||
val leftMinMax: Pair<Float, Float> = Pair(0f, 0f),
|
||||
val rightMinMax: Pair<Float, Float> = Pair(0f, 0f),
|
||||
val times: Pair<Int, Int> = Pair(0, 0)
|
||||
)
|
||||
|
||||
data class EnvironmentMetricsState(
|
||||
val environmentMetrics: List<Telemetry> = emptyList(),
|
||||
) {
|
||||
fun hasEnvironmentMetrics() = environmentMetrics.isNotEmpty()
|
||||
|
||||
/**
|
||||
* Filters [environmentMetrics] based on a [TimeFrame].
|
||||
*
|
||||
* @param timeFrame used to filter
|
||||
* @return [EnvironmentGraphingData]
|
||||
*/
|
||||
@Suppress("LongMethod")
|
||||
fun environmentMetricsFiltered(timeFrame: TimeFrame): EnvironmentGraphingData {
|
||||
val oldestTime = timeFrame.calculateOldestTime()
|
||||
val telemetries = environmentMetrics.filter { it.time >= oldestTime }
|
||||
val shouldPlot = BooleanArray(Environment.entries.size) { false }
|
||||
if (telemetries.isEmpty()) {
|
||||
return EnvironmentGraphingData(metrics = telemetries, shouldPlot = shouldPlot.toList())
|
||||
}
|
||||
|
||||
/* Grab the combined min and max for temp, humidity, and iaq. */
|
||||
val minValues = mutableListOf<Float>()
|
||||
val maxValues = mutableListOf<Float>()
|
||||
val (minTemp, maxTemp) = Pair(
|
||||
telemetries.minBy { it.environmentMetrics.temperature },
|
||||
telemetries.maxBy { it.environmentMetrics.temperature }
|
||||
)
|
||||
if (minTemp.environmentMetrics.temperature != 0f || maxTemp.environmentMetrics.temperature != 0f) {
|
||||
minValues.add(minTemp.environmentMetrics.temperature)
|
||||
maxValues.add(maxTemp.environmentMetrics.temperature)
|
||||
shouldPlot[Environment.TEMPERATURE.ordinal] = true
|
||||
}
|
||||
|
||||
val (minHumidity, maxHumidity) = Pair(
|
||||
telemetries.minBy { it.environmentMetrics.relativeHumidity },
|
||||
telemetries.maxBy { it.environmentMetrics.relativeHumidity }
|
||||
)
|
||||
if (minHumidity.environmentMetrics.relativeHumidity != 0f ||
|
||||
maxHumidity.environmentMetrics.relativeHumidity != 0f) {
|
||||
minValues.add(minHumidity.environmentMetrics.relativeHumidity)
|
||||
maxValues.add(maxHumidity.environmentMetrics.relativeHumidity)
|
||||
shouldPlot[Environment.HUMIDITY.ordinal] = true
|
||||
}
|
||||
|
||||
val (minIAQ, maxIAQ) = Pair(
|
||||
telemetries.minBy { it.environmentMetrics.iaq },
|
||||
telemetries.maxBy { it.environmentMetrics.iaq }
|
||||
)
|
||||
if (minIAQ.environmentMetrics.iaq != 0 || maxIAQ.environmentMetrics.iaq != 0) {
|
||||
minValues.add(minIAQ.environmentMetrics.iaq.toFloat())
|
||||
maxValues.add(maxIAQ.environmentMetrics.iaq.toFloat())
|
||||
shouldPlot[Environment.IAQ.ordinal] = true
|
||||
}
|
||||
|
||||
val min = minValues.minOf { it }
|
||||
val max = maxValues.maxOf { it }
|
||||
|
||||
val (minPressure, maxPressure) = Pair(
|
||||
telemetries.minBy { it.environmentMetrics.barometricPressure },
|
||||
telemetries.maxBy { it.environmentMetrics.barometricPressure }
|
||||
)
|
||||
if (minPressure.environmentMetrics.barometricPressure != 0.0F &&
|
||||
maxPressure.environmentMetrics.barometricPressure != 0.0F) {
|
||||
shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal] = true
|
||||
}
|
||||
val (oldest, newest) = Pair(
|
||||
telemetries.minBy { it.time },
|
||||
telemetries.maxBy { it.time }
|
||||
)
|
||||
|
||||
return EnvironmentGraphingData(
|
||||
metrics = telemetries,
|
||||
shouldPlot = shouldPlot.toList(),
|
||||
leftMinMax = Pair(
|
||||
minPressure.environmentMetrics.barometricPressure,
|
||||
maxPressure.environmentMetrics.barometricPressure
|
||||
),
|
||||
rightMinMax = Pair(min, max),
|
||||
times = Pair(oldest.time, newest.time)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -71,7 +71,6 @@ data class MetricsState(
|
|||
val displayUnits: DisplayUnits = DisplayUnits.METRIC,
|
||||
val node: Node? = null,
|
||||
val deviceMetrics: List<Telemetry> = emptyList(),
|
||||
val environmentMetrics: List<Telemetry> = emptyList(),
|
||||
val signalMetrics: List<MeshPacket> = emptyList(),
|
||||
val powerMetrics: List<Telemetry> = emptyList(),
|
||||
val tracerouteRequests: List<MeshLog> = emptyList(),
|
||||
|
|
@ -80,7 +79,6 @@ data class MetricsState(
|
|||
val deviceHardware: DeviceHardware? = null,
|
||||
) {
|
||||
fun hasDeviceMetrics() = deviceMetrics.isNotEmpty()
|
||||
fun hasEnvironmentMetrics() = environmentMetrics.isNotEmpty()
|
||||
fun hasSignalMetrics() = signalMetrics.isNotEmpty()
|
||||
fun hasPowerMetrics() = powerMetrics.isNotEmpty()
|
||||
fun hasTracerouteLogs() = tracerouteRequests.isNotEmpty()
|
||||
|
|
@ -91,11 +89,6 @@ data class MetricsState(
|
|||
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 }
|
||||
|
|
@ -217,6 +210,9 @@ class MetricsViewModel @Inject constructor(
|
|||
private val _state = MutableStateFlow(MetricsState.Empty)
|
||||
val state: StateFlow<MetricsState> = _state
|
||||
|
||||
private val _envState = MutableStateFlow(EnvironmentMetricsState())
|
||||
val environmentState: StateFlow<EnvironmentMetricsState> = _envState
|
||||
|
||||
private val _timeFrame = MutableStateFlow(TimeFrame.TWENTY_FOUR_HOURS)
|
||||
val timeFrame: StateFlow<TimeFrame> = _timeFrame
|
||||
|
||||
|
|
@ -253,12 +249,16 @@ class MetricsViewModel @Inject constructor(
|
|||
_state.update { state ->
|
||||
state.copy(
|
||||
deviceMetrics = telemetry.filter { it.hasDeviceMetrics() },
|
||||
powerMetrics = telemetry.filter { it.hasPowerMetrics() }
|
||||
)
|
||||
}
|
||||
_envState.update { state ->
|
||||
state.copy(
|
||||
environmentMetrics = telemetry.filter {
|
||||
it.hasEnvironmentMetrics() &&
|
||||
it.environmentMetrics.relativeHumidity >= 0f &&
|
||||
!it.environmentMetrics.temperature.isNaN()
|
||||
},
|
||||
powerMetrics = telemetry.filter { it.hasPowerMetrics() }
|
||||
)
|
||||
}
|
||||
}.launchIn(viewModelScope)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ import androidx.compose.material.icons.filled.Work
|
|||
import androidx.compose.material.icons.outlined.Navigation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -109,6 +110,20 @@ import com.geeksville.mesh.util.formatUptime
|
|||
import com.geeksville.mesh.util.thenIf
|
||||
import kotlin.math.ln
|
||||
|
||||
private enum class LogsType(
|
||||
val titleRes: Int,
|
||||
val icon: ImageVector,
|
||||
val route: Route
|
||||
) {
|
||||
DEVICE(R.string.device_metrics_log, Icons.Default.ChargingStation, Route.DeviceMetrics),
|
||||
NODE_MAP(R.string.node_map, Icons.Default.Map, Route.NodeMap),
|
||||
POSITIONS(R.string.position_log, Icons.Default.LocationOn, Route.PositionLog),
|
||||
ENVIRONMENT(R.string.env_metrics_log, Icons.Default.Thermostat, Route.EnvironmentMetrics),
|
||||
SIGNAL(R.string.sig_metrics_log, Icons.Default.SignalCellularAlt, Route.SignalMetrics),
|
||||
POWER(R.string.power_metrics_log, Icons.Default.Power, Route.PowerMetrics),
|
||||
TRACEROUTE(R.string.traceroute_log, Icons.Default.Route, Route.TracerouteLog)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NodeDetailScreen(
|
||||
modifier: Modifier = Modifier,
|
||||
|
|
@ -116,6 +131,19 @@ fun NodeDetailScreen(
|
|||
onNavigate: (Route) -> Unit,
|
||||
) {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
val environmentState by viewModel.environmentState.collectAsStateWithLifecycle()
|
||||
|
||||
/* The order is with respect to the enum above: LogsType */
|
||||
val availabilities = remember(key1 = state, key2 = environmentState) {
|
||||
booleanArrayOf(
|
||||
state.hasDeviceMetrics(),
|
||||
state.hasPositionLogs(),
|
||||
state.hasPositionLogs(),
|
||||
environmentState.hasEnvironmentMetrics(),
|
||||
state.hasSignalMetrics(),
|
||||
state.hasPowerMetrics(),
|
||||
state.hasTracerouteLogs())
|
||||
}
|
||||
|
||||
if (state.node != null) {
|
||||
val node = state.node ?: return
|
||||
|
|
@ -124,6 +152,7 @@ fun NodeDetailScreen(
|
|||
metricsState = state,
|
||||
onNavigate = onNavigate,
|
||||
modifier = modifier,
|
||||
metricsAvailability = availabilities
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
|
|
@ -141,6 +170,7 @@ private fun NodeDetailList(
|
|||
node: Node,
|
||||
metricsState: MetricsState,
|
||||
onNavigate: (Route) -> Unit = {},
|
||||
metricsAvailability: BooleanArray
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
|
|
@ -175,9 +205,18 @@ private fun NodeDetailList(
|
|||
}
|
||||
}
|
||||
|
||||
/* Metric Logs Navigation */
|
||||
item {
|
||||
PreferenceCategory(stringResource(id = R.string.logs))
|
||||
LogNavigationList(metricsState, onNavigate)
|
||||
for (type in LogsType.entries) {
|
||||
NavCard(
|
||||
title = stringResource(type.titleRes),
|
||||
icon = type.icon,
|
||||
enabled = metricsAvailability[type.ordinal]
|
||||
) {
|
||||
onNavigate(type.route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!metricsState.isManaged) {
|
||||
|
|
@ -345,65 +384,6 @@ private fun NodeDetailsContent(
|
|||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LogNavigationList(state: MetricsState, onNavigate: (Route) -> Unit) {
|
||||
NavCard(
|
||||
title = stringResource(R.string.device_metrics_log),
|
||||
icon = Icons.Default.ChargingStation,
|
||||
enabled = state.hasDeviceMetrics()
|
||||
) {
|
||||
onNavigate(Route.DeviceMetrics)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.node_map),
|
||||
icon = Icons.Default.Map,
|
||||
enabled = state.hasPositionLogs()
|
||||
) {
|
||||
onNavigate(Route.NodeMap)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.position_log),
|
||||
icon = Icons.Default.LocationOn,
|
||||
enabled = state.hasPositionLogs()
|
||||
) {
|
||||
onNavigate(Route.PositionLog)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.env_metrics_log),
|
||||
icon = Icons.Default.Thermostat,
|
||||
enabled = state.hasEnvironmentMetrics()
|
||||
) {
|
||||
onNavigate(Route.EnvironmentMetrics)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.sig_metrics_log),
|
||||
icon = Icons.Default.SignalCellularAlt,
|
||||
enabled = state.hasSignalMetrics()
|
||||
) {
|
||||
onNavigate(Route.SignalMetrics)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.power_metrics_log),
|
||||
icon = Icons.Default.Power,
|
||||
enabled = state.hasPowerMetrics()
|
||||
) {
|
||||
onNavigate(Route.PowerMetrics)
|
||||
}
|
||||
|
||||
NavCard(
|
||||
title = stringResource(R.string.traceroute_log),
|
||||
icon = Icons.Default.Route,
|
||||
enabled = state.hasTracerouteLogs()
|
||||
) {
|
||||
onNavigate(Route.TracerouteLog)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoCard(
|
||||
icon: ImageVector,
|
||||
|
|
@ -646,6 +626,7 @@ private fun NodeDetailsPreview(
|
|||
NodeDetailList(
|
||||
node = node,
|
||||
metricsState = MetricsState.Empty,
|
||||
metricsAvailability = BooleanArray(LogsType.entries.size) { false }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package com.geeksville.mesh.ui.components
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
|
|
@ -46,7 +47,6 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.res.stringResource
|
||||
|
|
@ -58,39 +58,15 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import com.geeksville.mesh.R
|
||||
import com.geeksville.mesh.TelemetryProtos.Telemetry
|
||||
import com.geeksville.mesh.copy
|
||||
import com.geeksville.mesh.model.Environment
|
||||
import com.geeksville.mesh.model.EnvironmentGraphingData
|
||||
import com.geeksville.mesh.model.MetricsViewModel
|
||||
import com.geeksville.mesh.model.TimeFrame
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.MS_PER_SEC
|
||||
import com.geeksville.mesh.ui.components.CommonCharts.DATE_TIME_FORMAT
|
||||
import com.geeksville.mesh.ui.theme.InfantryBlue
|
||||
import com.geeksville.mesh.ui.theme.Orange
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
import com.geeksville.mesh.util.GraphUtil.drawPathWithGradient
|
||||
|
||||
private enum class Environment(val color: Color) {
|
||||
TEMPERATURE(Color.Red) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.temperature
|
||||
}
|
||||
},
|
||||
HUMIDITY(InfantryBlue) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.relativeHumidity
|
||||
}
|
||||
},
|
||||
IAQ(Color.Green) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.iaq.toFloat()
|
||||
}
|
||||
},
|
||||
BAROMETRIC_PRESSURE(Orange) {
|
||||
override fun getValue(telemetry: Telemetry): Float {
|
||||
return telemetry.environmentMetrics.barometricPressure
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun getValue(telemetry: Telemetry): Float
|
||||
}
|
||||
private val LEGEND_DATA_1 = listOf(
|
||||
LegendData(
|
||||
nameRes = R.string.temperature,
|
||||
|
|
@ -121,8 +97,10 @@ fun EnvironmentMetricsScreen(
|
|||
viewModel: MetricsViewModel = hiltViewModel(),
|
||||
) {
|
||||
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||
val environmentState by viewModel.environmentState.collectAsStateWithLifecycle()
|
||||
val selectedTimeFrame by viewModel.timeFrame.collectAsState()
|
||||
val data = state.environmentMetricsFiltered(selectedTimeFrame)
|
||||
val graphData = environmentState.environmentMetricsFiltered(selectedTimeFrame)
|
||||
val data = graphData.metrics
|
||||
|
||||
/* Convert Celsius to Fahrenheit */
|
||||
@Suppress("MagicNumber")
|
||||
|
|
@ -161,6 +139,7 @@ fun EnvironmentMetricsScreen(
|
|||
.fillMaxWidth()
|
||||
.fillMaxHeight(fraction = 0.33f),
|
||||
telemetries = processedTelemetries.reversed(),
|
||||
graphData = graphData,
|
||||
selectedTimeFrame,
|
||||
promptInfoDialog = { displayInfoDialog = true }
|
||||
)
|
||||
|
|
@ -187,11 +166,14 @@ fun EnvironmentMetricsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "ComplexCondition")
|
||||
/* TODO need to take the time to understand this. */
|
||||
@SuppressLint("ConfigurationScreenWidthHeight")
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
private fun EnvironmentMetricsChart(
|
||||
modifier: Modifier = Modifier,
|
||||
telemetries: List<Telemetry>,
|
||||
graphData: EnvironmentGraphingData,
|
||||
selectedTime: TimeFrame,
|
||||
promptInfoDialog: () -> Unit
|
||||
) {
|
||||
|
|
@ -199,88 +181,37 @@ private fun EnvironmentMetricsChart(
|
|||
if (telemetries.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val (oldest, newest) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.time },
|
||||
telemetries.maxBy { it.time }
|
||||
)
|
||||
}
|
||||
val timeDiff = newest.time - oldest.time
|
||||
|
||||
val (oldest, newest) = graphData.times
|
||||
TimeLabels(
|
||||
oldest = oldest.time,
|
||||
newest = newest.time
|
||||
oldest = oldest,
|
||||
newest = newest
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
val graphColor = MaterialTheme.colors.onSurface
|
||||
|
||||
/* Grab the combined min and max for all data being plotted. */
|
||||
val (minTemp, maxTemp) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.environmentMetrics.temperature },
|
||||
telemetries.maxBy { it.environmentMetrics.temperature }
|
||||
)
|
||||
}
|
||||
val (minHumidity, maxHumidity) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.environmentMetrics.relativeHumidity },
|
||||
telemetries.maxBy { it.environmentMetrics.relativeHumidity }
|
||||
)
|
||||
}
|
||||
val minValues = mutableListOf(
|
||||
minTemp.environmentMetrics.temperature,
|
||||
minHumidity.environmentMetrics.relativeHumidity
|
||||
)
|
||||
val maxValues = mutableListOf(
|
||||
maxTemp.environmentMetrics.temperature,
|
||||
maxHumidity.environmentMetrics.relativeHumidity
|
||||
)
|
||||
val (minIAQ, maxIAQ) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.environmentMetrics.iaq },
|
||||
telemetries.maxBy { it.environmentMetrics.iaq }
|
||||
)
|
||||
}
|
||||
var plotIAQ = false
|
||||
if (minIAQ.environmentMetrics.iaq != 0 && maxIAQ.environmentMetrics.iaq != 0) {
|
||||
minValues.add(minIAQ.environmentMetrics.iaq.toFloat())
|
||||
maxValues.add(maxIAQ.environmentMetrics.iaq.toFloat())
|
||||
plotIAQ = true
|
||||
}
|
||||
|
||||
var min = minValues.minOf { it }
|
||||
val max = maxValues.maxOf { it }
|
||||
var diff = max - min
|
||||
|
||||
val (minPressure, maxPressure) = remember(key1 = telemetries) {
|
||||
Pair(
|
||||
telemetries.minBy { it.environmentMetrics.barometricPressure },
|
||||
telemetries.maxBy { it.environmentMetrics.barometricPressure }
|
||||
)
|
||||
}
|
||||
var plotPressure = false
|
||||
val pressureDiff =
|
||||
maxPressure.environmentMetrics.barometricPressure - minPressure.environmentMetrics.barometricPressure
|
||||
if (minPressure.environmentMetrics.barometricPressure != 0.0F &&
|
||||
maxPressure.environmentMetrics.barometricPressure != 0.0F) {
|
||||
plotPressure = true
|
||||
}
|
||||
val (rightMin, rightMax) = graphData.rightMinMax
|
||||
val (pressureMin, pressureMax) = graphData.leftMinMax
|
||||
var min = rightMin
|
||||
var diff = rightMax - rightMin
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp
|
||||
val timeDiff = newest - oldest
|
||||
val dp by remember(key1 = selectedTime) {
|
||||
mutableStateOf(selectedTime.dp(screenWidth, time = timeDiff.toLong()))
|
||||
}
|
||||
val shouldPlot = graphData.shouldPlot
|
||||
|
||||
Row {
|
||||
if (plotPressure) {
|
||||
if (shouldPlot[Environment.BAROMETRIC_PRESSURE.ordinal]) {
|
||||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
Environment.BAROMETRIC_PRESSURE.color,
|
||||
minValue = minPressure.environmentMetrics.barometricPressure,
|
||||
maxValue = maxPressure.environmentMetrics.barometricPressure
|
||||
minValue = pressureMin,
|
||||
maxValue = pressureMax
|
||||
)
|
||||
}
|
||||
Box(
|
||||
|
|
@ -297,8 +228,8 @@ private fun EnvironmentMetricsChart(
|
|||
|
||||
TimeAxisOverlay(
|
||||
modifier = modifier.width(dp),
|
||||
oldest = oldest.time,
|
||||
newest = newest.time,
|
||||
oldest = oldest,
|
||||
newest = newest,
|
||||
selectedTime.lineInterval()
|
||||
)
|
||||
|
||||
|
|
@ -309,13 +240,13 @@ private fun EnvironmentMetricsChart(
|
|||
var index: Int
|
||||
var first: Int
|
||||
for (metric in Environment.entries) {
|
||||
if (metric == Environment.IAQ && !plotIAQ ||
|
||||
metric == Environment.BAROMETRIC_PRESSURE && !plotPressure) {
|
||||
|
||||
if (!shouldPlot[metric.ordinal]) {
|
||||
continue
|
||||
}
|
||||
if (metric == Environment.BAROMETRIC_PRESSURE) {
|
||||
diff = pressureDiff
|
||||
min = minPressure.environmentMetrics.barometricPressure
|
||||
diff = pressureMax - pressureMin
|
||||
min = pressureMin
|
||||
}
|
||||
index = 0
|
||||
while (index < telemetries.size) {
|
||||
|
|
@ -325,7 +256,7 @@ private fun EnvironmentMetricsChart(
|
|||
telemetries = telemetries,
|
||||
index = index,
|
||||
path = path,
|
||||
oldestTime = oldest.time,
|
||||
oldestTime = oldest,
|
||||
timeRange = timeDiff,
|
||||
width = width,
|
||||
timeThreshold = selectedTime.timeThreshold()
|
||||
|
|
@ -339,8 +270,8 @@ private fun EnvironmentMetricsChart(
|
|||
path = path,
|
||||
color = metric.color,
|
||||
height = height,
|
||||
x1 = ((telemetries[index - 1].time - oldest.time).toFloat() / timeDiff) * width,
|
||||
x2 = ((telemetries[first].time - oldest.time).toFloat() / timeDiff) * width
|
||||
x1 = ((telemetries[index - 1].time - oldest).toFloat() / timeDiff) * width,
|
||||
x2 = ((telemetries[first].time - oldest).toFloat() / timeDiff) * width
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -349,8 +280,8 @@ private fun EnvironmentMetricsChart(
|
|||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
graphColor,
|
||||
minValue = min,
|
||||
maxValue = max
|
||||
minValue = rightMin,
|
||||
maxValue = rightMax
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue