mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(wire): migrate from protobuf -> wire (#4401)
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
9dbc8b7fbf
commit
25657e8f8f
239 changed files with 7149 additions and 6144 deletions
|
|
@ -27,10 +27,10 @@ import org.meshtastic.core.database.DatabaseManager
|
|||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.prefs.meshlog.MeshLogPrefs
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.MeshProtos.MeshPacket
|
||||
import org.meshtastic.proto.Portnums
|
||||
import org.meshtastic.proto.TelemetryProtos.Telemetry
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
import org.meshtastic.proto.MyNodeInfo
|
||||
import org.meshtastic.proto.PortNum
|
||||
import org.meshtastic.proto.Telemetry
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
@ -55,74 +55,39 @@ constructor(
|
|||
.conflate()
|
||||
|
||||
private fun parseTelemetryLog(log: MeshLog): Telemetry? = runCatching {
|
||||
Telemetry.parseFrom(log.fromRadio.packet.decoded.payload)
|
||||
.toBuilder()
|
||||
.apply {
|
||||
if (hasEnvironmentMetrics()) {
|
||||
// Handle float metrics that default to 0.0f when not explicitly set or when 0.0f means no
|
||||
// data
|
||||
if (!environmentMetrics.hasTemperature()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setTemperature(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasRelativeHumidity()) {
|
||||
environmentMetrics =
|
||||
environmentMetrics.toBuilder().setRelativeHumidity(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasSoilTemperature()) {
|
||||
environmentMetrics =
|
||||
environmentMetrics.toBuilder().setSoilTemperature(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasBarometricPressure()) {
|
||||
environmentMetrics =
|
||||
environmentMetrics.toBuilder().setBarometricPressure(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasGasResistance()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setGasResistance(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasVoltage()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setVoltage(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasCurrent()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setCurrent(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasLux()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setLux(Float.NaN).build()
|
||||
}
|
||||
if (!environmentMetrics.hasUvLux()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setUvLux(Float.NaN).build()
|
||||
}
|
||||
|
||||
// Handle uint32 metrics that default to 0 when not explicitly set or when 0 means no data
|
||||
if (!environmentMetrics.hasIaq()) {
|
||||
environmentMetrics = environmentMetrics.toBuilder().setIaq(Int.MIN_VALUE).build()
|
||||
}
|
||||
if (!environmentMetrics.hasSoilMoisture()) {
|
||||
environmentMetrics =
|
||||
environmentMetrics.toBuilder().setSoilMoisture(Int.MIN_VALUE).build()
|
||||
}
|
||||
}
|
||||
// Leaving in case we have need of nulling any in device metrics.
|
||||
// if (hasDeviceMetrics()) {
|
||||
// deviceMetrics =
|
||||
// deviceMetrics.toBuilder().setBatteryLevel(Int.MIN_VALUE).build()
|
||||
// }
|
||||
}
|
||||
.setTime((log.received_date / MILLIS_TO_SECONDS).toInt())
|
||||
.build()
|
||||
val payload = log.fromRadio.packet?.decoded?.payload ?: return@runCatching null
|
||||
val telemetry = Telemetry.ADAPTER.decode(payload)
|
||||
telemetry.copy(
|
||||
time = (log.received_date / MILLIS_TO_SECONDS).toInt(),
|
||||
environment_metrics =
|
||||
telemetry.environment_metrics?.let { metrics ->
|
||||
metrics.copy(
|
||||
temperature = metrics.temperature ?: Float.NaN,
|
||||
relative_humidity = metrics.relative_humidity ?: Float.NaN,
|
||||
soil_temperature = metrics.soil_temperature ?: Float.NaN,
|
||||
barometric_pressure = metrics.barometric_pressure ?: Float.NaN,
|
||||
gas_resistance = metrics.gas_resistance ?: Float.NaN,
|
||||
voltage = metrics.voltage ?: Float.NaN,
|
||||
current = metrics.current ?: Float.NaN,
|
||||
lux = metrics.lux ?: Float.NaN,
|
||||
uv_lux = metrics.uv_lux ?: Float.NaN,
|
||||
iaq = metrics.iaq ?: Int.MIN_VALUE,
|
||||
soil_moisture = metrics.soil_moisture ?: Int.MIN_VALUE,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
.getOrNull()
|
||||
|
||||
fun getTelemetryFrom(nodeNum: Int): Flow<List<Telemetry>> = dbManager.currentDb
|
||||
.flatMapLatest {
|
||||
it.meshLogDao().getLogsFrom(nodeNum, Portnums.PortNum.TELEMETRY_APP_VALUE, MAX_MESH_PACKETS)
|
||||
}
|
||||
.flatMapLatest { it.meshLogDao().getLogsFrom(nodeNum, PortNum.TELEMETRY_APP.value, MAX_MESH_PACKETS) }
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { list -> list.mapNotNull(::parseTelemetryLog) }
|
||||
.flowOn(dispatchers.io)
|
||||
|
||||
fun getLogsFrom(
|
||||
nodeNum: Int,
|
||||
portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE,
|
||||
portNum: Int = PortNum.UNKNOWN_APP.value,
|
||||
maxItem: Int = MAX_MESH_PACKETS,
|
||||
): Flow<List<MeshLog>> = dbManager.currentDb
|
||||
.flatMapLatest { it.meshLogDao().getLogsFrom(nodeNum, portNum, maxItem) }
|
||||
|
|
@ -133,10 +98,12 @@ constructor(
|
|||
* Retrieves MeshPackets matching 'nodeNum' and 'portNum'.
|
||||
* If 'portNum' is not specified, returns all MeshPackets. Otherwise, filters by 'portNum'.
|
||||
*/
|
||||
fun getMeshPacketsFrom(nodeNum: Int, portNum: Int = Portnums.PortNum.UNKNOWN_APP_VALUE): Flow<List<MeshPacket>> =
|
||||
getLogsFrom(nodeNum, portNum).mapLatest { list -> list.map { it.fromRadio.packet } }.flowOn(dispatchers.io)
|
||||
fun getMeshPacketsFrom(nodeNum: Int, portNum: Int = PortNum.UNKNOWN_APP.value): Flow<List<MeshPacket>> =
|
||||
getLogsFrom(nodeNum, portNum)
|
||||
.mapLatest { list -> list.mapNotNull { it.fromRadio.packet } }
|
||||
.flowOn(dispatchers.io)
|
||||
|
||||
fun getMyNodeInfo(): Flow<MeshProtos.MyNodeInfo?> = getLogsFrom(0, 0)
|
||||
fun getMyNodeInfo(): Flow<MyNodeInfo?> = getLogsFrom(0, 0)
|
||||
.mapLatest { list -> list.firstOrNull { it.myNodeInfo != null }?.myNodeInfo }
|
||||
.flowOn(dispatchers.io)
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,8 @@ import org.meshtastic.core.di.CoroutineDispatchers
|
|||
import org.meshtastic.core.di.ProcessLifecycle
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.util.onlineTimeThreshold
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.HardwareModel
|
||||
import org.meshtastic.proto.User
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -107,27 +108,25 @@ constructor(
|
|||
fun getNode(userId: String): Node = nodeDBbyNum.value.values.find { it.user.id == userId }
|
||||
?: Node(num = DataPacket.idToDefaultNodeNum(userId) ?: 0, user = getUser(userId))
|
||||
|
||||
fun getUser(nodeNum: Int): MeshProtos.User = getUser(DataPacket.nodeNumToDefaultId(nodeNum))
|
||||
fun getUser(nodeNum: Int): User = getUser(DataPacket.nodeNumToDefaultId(nodeNum))
|
||||
|
||||
fun getUser(userId: String): MeshProtos.User = nodeDBbyNum.value.values.find { it.user.id == userId }?.user
|
||||
?: MeshProtos.User.newBuilder()
|
||||
.setId(userId)
|
||||
.setLongName(
|
||||
if (userId == DataPacket.ID_LOCAL) {
|
||||
ourNodeInfo.value?.user?.longName ?: "Local"
|
||||
} else {
|
||||
"Meshtastic ${userId.takeLast(n = 4)}"
|
||||
},
|
||||
)
|
||||
.setShortName(
|
||||
if (userId == DataPacket.ID_LOCAL) {
|
||||
ourNodeInfo.value?.user?.shortName ?: "Local"
|
||||
} else {
|
||||
userId.takeLast(n = 4)
|
||||
},
|
||||
)
|
||||
.setHwModel(MeshProtos.HardwareModel.UNSET)
|
||||
.build()
|
||||
fun getUser(userId: String): User = nodeDBbyNum.value.values.find { it.user.id == userId }?.user
|
||||
?: User(
|
||||
id = userId,
|
||||
long_name =
|
||||
if (userId == DataPacket.ID_LOCAL) {
|
||||
ourNodeInfo.value?.user?.long_name ?: "Local"
|
||||
} else {
|
||||
"Meshtastic ${userId.takeLast(n = 4)}"
|
||||
},
|
||||
short_name =
|
||||
if (userId == DataPacket.ID_LOCAL) {
|
||||
ourNodeInfo.value?.user?.short_name ?: "Local"
|
||||
} else {
|
||||
userId.takeLast(n = 4)
|
||||
},
|
||||
hw_model = HardwareModel.UNSET,
|
||||
)
|
||||
|
||||
fun getNodes(
|
||||
sort: NodeSortOption = NodeSortOption.LAST_HEARD,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import kotlinx.coroutines.flow.flatMapLatest
|
|||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.withContext
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.ContactSettings
|
||||
import org.meshtastic.core.database.entity.Packet
|
||||
|
|
@ -34,8 +35,8 @@ import org.meshtastic.core.database.model.Node
|
|||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.model.DataPacket
|
||||
import org.meshtastic.core.model.MessageStatus
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.Portnums.PortNum
|
||||
import org.meshtastic.proto.ChannelSettings
|
||||
import org.meshtastic.proto.PortNum
|
||||
import javax.inject.Inject
|
||||
|
||||
class PacketRepository
|
||||
|
|
@ -184,6 +185,8 @@ constructor(
|
|||
DataPacket.nodeNumToDefaultId(to)
|
||||
}
|
||||
|
||||
val hashByteString = hash.toByteString()
|
||||
|
||||
packets.forEach { packet ->
|
||||
// For sent messages, from is stored as ID_LOCAL, but SFPP packet has node number
|
||||
val fromMatches =
|
||||
|
|
@ -199,8 +202,8 @@ constructor(
|
|||
return@forEach
|
||||
}
|
||||
val newTime = if (rxTime > 0) rxTime * MILLISECONDS_IN_SECOND else packet.received_time
|
||||
val updatedData = packet.data.copy(status = status, sfppHash = hash, time = newTime)
|
||||
dao.update(packet.copy(data = updatedData, sfpp_hash = hash, received_time = newTime))
|
||||
val updatedData = packet.data.copy(status = status, sfppHash = hashByteString, time = newTime)
|
||||
dao.update(packet.copy(data = updatedData, sfpp_hash = hashByteString, received_time = newTime))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +225,8 @@ constructor(
|
|||
return@forEach
|
||||
}
|
||||
val newTime = if (rxTime > 0) rxTime * MILLISECONDS_IN_SECOND else reaction.timestamp
|
||||
val updatedReaction = reaction.copy(status = status, sfpp_hash = hash, timestamp = newTime)
|
||||
val updatedReaction =
|
||||
reaction.copy(status = status, sfpp_hash = hashByteString, timestamp = newTime)
|
||||
dao.update(updatedReaction)
|
||||
}
|
||||
}
|
||||
|
|
@ -234,22 +238,23 @@ constructor(
|
|||
rxTime: Long = 0,
|
||||
) = withContext(dispatchers.io) {
|
||||
val dao = dbManager.currentDb.value.packetDao()
|
||||
dao.findPacketBySfppHash(hash)?.let { packet ->
|
||||
val hashByteString = hash.toByteString()
|
||||
dao.findPacketBySfppHash(hashByteString)?.let { packet ->
|
||||
// If it's already confirmed, don't downgrade it
|
||||
if (packet.data.status == MessageStatus.SFPP_CONFIRMED && status == MessageStatus.SFPP_ROUTING) {
|
||||
return@let
|
||||
}
|
||||
val newTime = if (rxTime > 0) rxTime * MILLISECONDS_IN_SECOND else packet.received_time
|
||||
val updatedData = packet.data.copy(status = status, sfppHash = hash, time = newTime)
|
||||
dao.update(packet.copy(data = updatedData, sfpp_hash = hash, received_time = newTime))
|
||||
val updatedData = packet.data.copy(status = status, sfppHash = hashByteString, time = newTime)
|
||||
dao.update(packet.copy(data = updatedData, sfpp_hash = hashByteString, received_time = newTime))
|
||||
}
|
||||
|
||||
dao.findReactionBySfppHash(hash)?.let { reaction ->
|
||||
dao.findReactionBySfppHash(hashByteString)?.let { reaction ->
|
||||
if (reaction.status == MessageStatus.SFPP_CONFIRMED && status == MessageStatus.SFPP_ROUTING) {
|
||||
return@let
|
||||
}
|
||||
val newTime = if (rxTime > 0) rxTime * MILLISECONDS_IN_SECOND else reaction.timestamp
|
||||
val updatedReaction = reaction.copy(status = status, sfpp_hash = hash, timestamp = newTime)
|
||||
val updatedReaction = reaction.copy(status = status, sfpp_hash = hashByteString, timestamp = newTime)
|
||||
dao.update(updatedReaction)
|
||||
}
|
||||
}
|
||||
|
|
@ -340,7 +345,7 @@ constructor(
|
|||
}
|
||||
|
||||
private fun org.meshtastic.core.database.dao.PacketDao.getAllWaypointsFlow(): Flow<List<Packet>> =
|
||||
getAllPackets(PortNum.WAYPOINT_APP_VALUE)
|
||||
getAllPackets(PortNum.WAYPOINT_APP.value)
|
||||
|
||||
companion object {
|
||||
private const val CONTACTS_PAGE_SIZE = 30
|
||||
|
|
|
|||
|
|
@ -22,15 +22,14 @@ import org.meshtastic.core.datastore.ChannelSetDataSource
|
|||
import org.meshtastic.core.datastore.LocalConfigDataSource
|
||||
import org.meshtastic.core.datastore.ModuleConfigDataSource
|
||||
import org.meshtastic.core.model.util.getChannelUrl
|
||||
import org.meshtastic.proto.AppOnlyProtos.ChannelSet
|
||||
import org.meshtastic.proto.ChannelProtos.Channel
|
||||
import org.meshtastic.proto.ChannelProtos.ChannelSettings
|
||||
import org.meshtastic.proto.ClientOnlyProtos.DeviceProfile
|
||||
import org.meshtastic.proto.ConfigProtos.Config
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalConfig
|
||||
import org.meshtastic.proto.LocalOnlyProtos.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfigProtos.ModuleConfig
|
||||
import org.meshtastic.proto.deviceProfile
|
||||
import org.meshtastic.proto.Channel
|
||||
import org.meshtastic.proto.ChannelSet
|
||||
import org.meshtastic.proto.ChannelSettings
|
||||
import org.meshtastic.proto.Config
|
||||
import org.meshtastic.proto.DeviceProfile
|
||||
import org.meshtastic.proto.LocalConfig
|
||||
import org.meshtastic.proto.LocalModuleConfig
|
||||
import org.meshtastic.proto.ModuleConfig
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
|
@ -83,7 +82,7 @@ constructor(
|
|||
*/
|
||||
suspend fun setLocalConfig(config: Config) {
|
||||
localConfigDataSource.setLocalConfig(config)
|
||||
if (config.hasLora()) channelSetDataSource.setLoraConfig(config.lora)
|
||||
config.lora?.let { channelSetDataSource.setLoraConfig(it) }
|
||||
}
|
||||
|
||||
/** Flow representing the [LocalModuleConfig] data store. */
|
||||
|
|
@ -111,17 +110,18 @@ constructor(
|
|||
localConfig,
|
||||
localModuleConfig,
|
||||
->
|
||||
deviceProfile {
|
||||
node?.user?.let {
|
||||
longName = it.longName
|
||||
shortName = it.shortName
|
||||
}
|
||||
channelUrl = channels.getChannelUrl().toString()
|
||||
config = localConfig
|
||||
moduleConfig = localModuleConfig
|
||||
if (node != null && localConfig.position.fixedPosition) {
|
||||
fixedPosition = node.position
|
||||
}
|
||||
}
|
||||
DeviceProfile(
|
||||
long_name = node?.user?.long_name,
|
||||
short_name = node?.user?.short_name,
|
||||
channel_url = channels.getChannelUrl().toString(),
|
||||
config = localConfig,
|
||||
module_config = localModuleConfig,
|
||||
fixed_position =
|
||||
if (node != null && localConfig.position?.fixed_position == true) {
|
||||
node.position
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Meshtastic LLC
|
||||
* Copyright (c) 2025-2026 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
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
* 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 org.meshtastic.core.data.repository
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -27,7 +26,7 @@ import kotlinx.coroutines.withContext
|
|||
import org.meshtastic.core.database.DatabaseManager
|
||||
import org.meshtastic.core.database.entity.TracerouteNodePositionEntity
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.proto.MeshProtos
|
||||
import org.meshtastic.proto.Position
|
||||
import javax.inject.Inject
|
||||
|
||||
class TracerouteSnapshotRepository
|
||||
|
|
@ -37,14 +36,14 @@ constructor(
|
|||
private val dispatchers: CoroutineDispatchers,
|
||||
) {
|
||||
|
||||
fun getSnapshotPositions(logUuid: String): Flow<Map<Int, MeshProtos.Position>> = dbManager.currentDb
|
||||
fun getSnapshotPositions(logUuid: String): Flow<Map<Int, Position>> = dbManager.currentDb
|
||||
.flatMapLatest { it.tracerouteNodePositionDao().getByLogUuid(logUuid) }
|
||||
.distinctUntilChanged()
|
||||
.mapLatest { list -> list.associate { it.nodeNum to it.position } }
|
||||
.flowOn(dispatchers.io)
|
||||
.conflate()
|
||||
|
||||
suspend fun upsertSnapshotPositions(logUuid: String, requestId: Int, positions: Map<Int, MeshProtos.Position>) =
|
||||
suspend fun upsertSnapshotPositions(logUuid: String, requestId: Int, positions: Map<Int, Position>) =
|
||||
withContext(dispatchers.io) {
|
||||
val dao = dbManager.currentDb.value.tracerouteNodePositionDao()
|
||||
dao.deleteByLogUuid(logUuid)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import io.mockk.mockk
|
|||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Test
|
||||
|
|
@ -30,12 +31,12 @@ import org.meshtastic.core.database.dao.MeshLogDao
|
|||
import org.meshtastic.core.database.entity.MeshLog
|
||||
import org.meshtastic.core.di.CoroutineDispatchers
|
||||
import org.meshtastic.core.prefs.meshlog.MeshLogPrefs
|
||||
import org.meshtastic.proto.MeshProtos.Data
|
||||
import org.meshtastic.proto.MeshProtos.FromRadio
|
||||
import org.meshtastic.proto.MeshProtos.MeshPacket
|
||||
import org.meshtastic.proto.Portnums.PortNum
|
||||
import org.meshtastic.proto.TelemetryProtos.EnvironmentMetrics
|
||||
import org.meshtastic.proto.TelemetryProtos.Telemetry
|
||||
import org.meshtastic.proto.Data
|
||||
import org.meshtastic.proto.EnvironmentMetrics
|
||||
import org.meshtastic.proto.FromRadio
|
||||
import org.meshtastic.proto.MeshPacket
|
||||
import org.meshtastic.proto.PortNum
|
||||
import org.meshtastic.proto.Telemetry
|
||||
import java.util.UUID
|
||||
|
||||
class MeshLogRepositoryTest {
|
||||
|
|
@ -57,15 +58,10 @@ class MeshLogRepositoryTest {
|
|||
@Test
|
||||
fun `parseTelemetryLog preserves zero temperature`() = runTest(testDispatcher) {
|
||||
val zeroTemp = 0.0f
|
||||
val envMetrics = EnvironmentMetrics.newBuilder().setTemperature(zeroTemp).build()
|
||||
val telemetry = Telemetry.newBuilder().setEnvironmentMetrics(envMetrics).build()
|
||||
val telemetry = Telemetry(environment_metrics = EnvironmentMetrics(temperature = zeroTemp))
|
||||
|
||||
val meshPacket =
|
||||
MeshPacket.newBuilder()
|
||||
.setDecoded(
|
||||
Data.newBuilder().setPayload(telemetry.toByteString()).setPortnum(PortNum.TELEMETRY_APP),
|
||||
)
|
||||
.build()
|
||||
MeshPacket(decoded = Data(payload = telemetry.encode().toByteString(), portnum = PortNum.TELEMETRY_APP))
|
||||
|
||||
val meshLog =
|
||||
MeshLog(
|
||||
|
|
@ -73,7 +69,7 @@ class MeshLogRepositoryTest {
|
|||
message_type = "telemetry",
|
||||
received_date = System.currentTimeMillis(),
|
||||
raw_message = "",
|
||||
fromRadio = FromRadio.newBuilder().setPacket(meshPacket).build(),
|
||||
fromRadio = FromRadio(packet = meshPacket),
|
||||
)
|
||||
|
||||
// Using reflection to test private method parseTelemetryLog
|
||||
|
|
@ -82,22 +78,17 @@ class MeshLogRepositoryTest {
|
|||
val result = method.invoke(repository, meshLog) as Telemetry?
|
||||
|
||||
assertNotNull(result)
|
||||
val resultMetrics = result?.environmentMetrics
|
||||
val resultMetrics = result?.environment_metrics
|
||||
assertNotNull(resultMetrics)
|
||||
assertEquals(zeroTemp, resultMetrics?.temperature!!, 0.01f)
|
||||
assertEquals(zeroTemp, resultMetrics?.temperature ?: 0f, 0.01f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parseTelemetryLog maps missing temperature to NaN`() = runTest(testDispatcher) {
|
||||
val envMetrics = EnvironmentMetrics.newBuilder().build() // Temperature not set
|
||||
val telemetry = Telemetry.newBuilder().setEnvironmentMetrics(envMetrics).build()
|
||||
val telemetry = Telemetry(environment_metrics = EnvironmentMetrics(temperature = null))
|
||||
|
||||
val meshPacket =
|
||||
MeshPacket.newBuilder()
|
||||
.setDecoded(
|
||||
Data.newBuilder().setPayload(telemetry.toByteString()).setPortnum(PortNum.TELEMETRY_APP),
|
||||
)
|
||||
.build()
|
||||
MeshPacket(decoded = Data(payload = telemetry.encode().toByteString(), portnum = PortNum.TELEMETRY_APP))
|
||||
|
||||
val meshLog =
|
||||
MeshLog(
|
||||
|
|
@ -105,7 +96,7 @@ class MeshLogRepositoryTest {
|
|||
message_type = "telemetry",
|
||||
received_date = System.currentTimeMillis(),
|
||||
raw_message = "",
|
||||
fromRadio = FromRadio.newBuilder().setPacket(meshPacket).build(),
|
||||
fromRadio = FromRadio(packet = meshPacket),
|
||||
)
|
||||
|
||||
val method = MeshLogRepository::class.java.getDeclaredMethod("parseTelemetryLog", MeshLog::class.java)
|
||||
|
|
@ -113,9 +104,9 @@ class MeshLogRepositoryTest {
|
|||
val result = method.invoke(repository, meshLog) as Telemetry?
|
||||
|
||||
assertNotNull(result)
|
||||
val resultMetrics = result?.environmentMetrics
|
||||
val resultMetrics = result?.environment_metrics
|
||||
|
||||
// Should be NaN as per repository logic for missing fields
|
||||
assertEquals(Float.NaN, resultMetrics?.temperature!!, 0.01f)
|
||||
assertEquals(Float.NaN, resultMetrics?.temperature ?: 0f, 0.01f)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue