kopia lustrzana https://github.com/meshtastic/Meshtastic-Android
feat: Graph Barometric Pressure (#1684)
* refactor: condensed the process of drawing the lines in the env metrics * feat: only include iaq in the env metric graph when we have actual values * feat: plotting barometric pressure when we have actual values * detekt: spacing after ( --------- Co-authored-by: James Rich <2199651+jamesarich@users.noreply.github.com>pull/1691/head
rodzic
903e304964
commit
e839c43542
|
@ -66,7 +66,6 @@ object CommonCharts {
|
|||
val DATE_TIME_FORMAT: DateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)
|
||||
const val MS_PER_SEC = 1000L
|
||||
const val MAX_PERCENT_VALUE = 100f
|
||||
val INFANTRY_BLUE = Color(75, 119, 190)
|
||||
}
|
||||
|
||||
private const val LINE_ON = 10f
|
||||
|
|
|
@ -62,16 +62,36 @@ 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.components.CommonCharts.INFANTRY_BLUE
|
||||
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),
|
||||
HUMIDITY(INFANTRY_BLUE),
|
||||
IAQ(Color.Green)
|
||||
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 = listOf(
|
||||
private val LEGEND_DATA_1 = listOf(
|
||||
LegendData(
|
||||
nameRes = R.string.temperature,
|
||||
color = Environment.TEMPERATURE.color,
|
||||
|
@ -82,11 +102,18 @@ private val LEGEND_DATA = listOf(
|
|||
color = Environment.HUMIDITY.color,
|
||||
isLine = true
|
||||
),
|
||||
)
|
||||
private val LEGEND_DATA_2 = listOf(
|
||||
LegendData(
|
||||
nameRes = R.string.iaq,
|
||||
color = Environment.IAQ.color,
|
||||
isLine = true
|
||||
),
|
||||
LegendData(
|
||||
nameRes = R.string.baro_pressure,
|
||||
color = Environment.BAROMETRIC_PRESSURE.color,
|
||||
isLine = true
|
||||
)
|
||||
)
|
||||
|
||||
@Composable
|
||||
|
@ -160,7 +187,7 @@ fun EnvironmentMetricsScreen(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod")
|
||||
@Suppress("LongMethod", "CyclomaticComplexMethod", "ComplexCondition")
|
||||
@Composable
|
||||
private fun EnvironmentMetricsChart(
|
||||
modifier: Modifier = Modifier,
|
||||
|
@ -202,23 +229,44 @@ private fun EnvironmentMetricsChart(
|
|||
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 }
|
||||
)
|
||||
}
|
||||
val min = minOf(
|
||||
minTemp.environmentMetrics.temperature,
|
||||
minHumidity.environmentMetrics.relativeHumidity,
|
||||
minIAQ.environmentMetrics.iaq.toFloat()
|
||||
)
|
||||
val max = maxOf(
|
||||
maxTemp.environmentMetrics.temperature,
|
||||
maxHumidity.environmentMetrics.relativeHumidity,
|
||||
maxIAQ.environmentMetrics.iaq.toFloat()
|
||||
)
|
||||
val diff = max - min
|
||||
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
|
||||
}
|
||||
|
||||
val 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 scrollState = rememberScrollState()
|
||||
val screenWidth = LocalConfiguration.current.screenWidthDp
|
||||
|
@ -227,6 +275,14 @@ private fun EnvironmentMetricsChart(
|
|||
}
|
||||
|
||||
Row {
|
||||
if (plotPressure) {
|
||||
YAxisLabels(
|
||||
modifier = modifier.weight(weight = .1f),
|
||||
Environment.BAROMETRIC_PRESSURE.color,
|
||||
minValue = minPressure.environmentMetrics.barometricPressure,
|
||||
maxValue = maxPressure.environmentMetrics.barometricPressure
|
||||
)
|
||||
}
|
||||
Box(
|
||||
contentAlignment = Alignment.TopStart,
|
||||
modifier = Modifier
|
||||
|
@ -250,89 +306,42 @@ private fun EnvironmentMetricsChart(
|
|||
val height = size.height
|
||||
val width = size.width
|
||||
|
||||
/* Temperature */
|
||||
var index = 0
|
||||
var index: Int
|
||||
var first: Int
|
||||
while (index < telemetries.size) {
|
||||
first = index
|
||||
val path = Path()
|
||||
index = createPath(
|
||||
telemetries = telemetries,
|
||||
index = index,
|
||||
path = path,
|
||||
oldestTime = oldest.time,
|
||||
timeRange = timeDiff,
|
||||
width = width,
|
||||
timeThreshold = selectedTime.timeThreshold()
|
||||
) { i ->
|
||||
val telemetry = telemetries.getOrNull(i) ?: telemetries.last()
|
||||
val ratio = (telemetry.environmentMetrics.temperature - min) / diff
|
||||
val y = height - (ratio * height)
|
||||
return@createPath y
|
||||
for (metric in Environment.entries) {
|
||||
if (metric == Environment.IAQ && !plotIAQ ||
|
||||
metric == Environment.BAROMETRIC_PRESSURE && !plotPressure) {
|
||||
continue
|
||||
}
|
||||
drawPathWithGradient(
|
||||
path = path,
|
||||
color = Environment.TEMPERATURE.color,
|
||||
height = height,
|
||||
x1 = ((telemetries[index - 1].time - oldest.time).toFloat() / timeDiff) * width,
|
||||
x2 = ((telemetries[first].time - oldest.time).toFloat() / timeDiff) * width
|
||||
)
|
||||
}
|
||||
|
||||
/* Relative Humidity */
|
||||
index = 0
|
||||
while (index < telemetries.size) {
|
||||
first = index
|
||||
val path = Path()
|
||||
index = createPath(
|
||||
telemetries = telemetries,
|
||||
index = index,
|
||||
path = path,
|
||||
oldestTime = oldest.time,
|
||||
timeRange = timeDiff,
|
||||
width = width,
|
||||
timeThreshold = selectedTime.timeThreshold()
|
||||
) { i ->
|
||||
val telemetry = telemetries.getOrNull(i) ?: telemetries.last()
|
||||
val ratio = (telemetry.environmentMetrics.relativeHumidity - min) / diff
|
||||
val y = height - (ratio * height)
|
||||
return@createPath y
|
||||
if (metric == Environment.BAROMETRIC_PRESSURE) {
|
||||
diff = pressureDiff
|
||||
}
|
||||
drawPathWithGradient(
|
||||
path = path,
|
||||
color = Environment.HUMIDITY.color,
|
||||
height = height,
|
||||
x1 = ((telemetries[index - 1].time - oldest.time).toFloat() / timeDiff) * width,
|
||||
x2 = ((telemetries[first].time - oldest.time).toFloat() / timeDiff) * width
|
||||
)
|
||||
}
|
||||
|
||||
/* Air Quality */
|
||||
index = 0
|
||||
while (index < telemetries.size) {
|
||||
first = index
|
||||
val path = Path()
|
||||
index = createPath(
|
||||
telemetries = telemetries,
|
||||
index = index,
|
||||
path = path,
|
||||
oldestTime = oldest.time,
|
||||
timeRange = timeDiff,
|
||||
width = width,
|
||||
timeThreshold = selectedTime.timeThreshold()
|
||||
) { i ->
|
||||
val telemetry = telemetries.getOrNull(i) ?: telemetries.last()
|
||||
val ratio = (telemetry.environmentMetrics.iaq - min) / diff
|
||||
val y = height - (ratio * height)
|
||||
return@createPath y
|
||||
index = 0
|
||||
while (index < telemetries.size) {
|
||||
first = index
|
||||
val path = Path()
|
||||
index = createPath(
|
||||
telemetries = telemetries,
|
||||
index = index,
|
||||
path = path,
|
||||
oldestTime = oldest.time,
|
||||
timeRange = timeDiff,
|
||||
width = width,
|
||||
timeThreshold = selectedTime.timeThreshold()
|
||||
) { i ->
|
||||
val telemetry = telemetries.getOrNull(i) ?: telemetries.last()
|
||||
val ratio = (metric.getValue(telemetry) - min) / diff
|
||||
val y = height - (ratio * height)
|
||||
return@createPath y
|
||||
}
|
||||
drawPathWithGradient(
|
||||
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
|
||||
)
|
||||
}
|
||||
drawPathWithGradient(
|
||||
path = path,
|
||||
color = Environment.IAQ.color,
|
||||
height = height,
|
||||
x1 = ((telemetries[index - 1].time - oldest.time).toFloat() / timeDiff) * width,
|
||||
x2 = ((telemetries[first].time - oldest.time).toFloat() / timeDiff) * width
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -346,7 +355,8 @@ private fun EnvironmentMetricsChart(
|
|||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
Legend(LEGEND_DATA, promptInfoDialog = promptInfoDialog)
|
||||
Legend(LEGEND_DATA_1, displayInfoIcon = false)
|
||||
Legend(LEGEND_DATA_2, promptInfoDialog = promptInfoDialog)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
|
|
|
@ -64,13 +64,13 @@ 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.components.CommonCharts.INFANTRY_BLUE
|
||||
import com.geeksville.mesh.ui.theme.InfantryBlue
|
||||
import com.geeksville.mesh.util.GraphUtil
|
||||
import com.geeksville.mesh.util.GraphUtil.createPath
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private enum class Power(val color: Color, val min: Float, val max: Float) {
|
||||
CURRENT(INFANTRY_BLUE, -500f, 500f),
|
||||
CURRENT(InfantryBlue, -500f, 500f),
|
||||
VOLTAGE(Color.Red, 0f, 20f);
|
||||
/**
|
||||
* Difference between the metrics `max` and `min` values.
|
||||
|
|
|
@ -35,4 +35,5 @@ val LightRed = Color(0xFFFFB3B3)
|
|||
val MeshtasticGreen = Color(0xFF67EA94)
|
||||
|
||||
val HyperlinkBlue = Color(0xFF43C3B0)
|
||||
val Orange = Color(255, 153, 0)
|
||||
val InfantryBlue = Color(red = 75, green = 119, blue = 190)
|
||||
val Orange = Color(red = 247, green = 147, blue = 26)
|
|
@ -341,6 +341,7 @@
|
|||
<string name="meshtastic_low_battery_notifications">Low battery notifications</string>
|
||||
<string name="low_battery_title">Low battery: %s</string>
|
||||
<string name="meshtastic_low_battery_temporary_remote_notifications">Low battery notifications (favorite nodes)</string>
|
||||
<string name="baro_pressure">Barometric Pressure</string>
|
||||
<string name="mesh_via_udp_enabled">Mesh via UDP enabled</string>
|
||||
<string name="udp_config">UDP Config</string>
|
||||
</resources>
|
||||
|
|
Ładowanie…
Reference in New Issue