refactor: replace service local node db with Room NodeDB

pull/1243/head
andrekir 2024-09-13 00:19:21 -03:00
rodzic 84939a74d2
commit 39a18e6418
3 zmienionych plików z 31 dodań i 92 usunięć

Wyświetl plik

@ -70,13 +70,15 @@ class NodeDB @Inject constructor(
nodeInfoDao.upsert(node) nodeInfoDao.upsert(node)
} }
suspend fun clearNodeDB() = withContext(Dispatchers.IO) {
nodeInfoDao.clearNodeInfo()
nodeInfoDao.clearMyNodeInfo()
}
suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) = withContext(Dispatchers.IO) { suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) = withContext(Dispatchers.IO) {
nodeInfoDao.apply { clearNodeDB()
clearNodeInfo() nodeInfoDao.setMyNodeInfo(mi) // set MyNodeInfo first
clearMyNodeInfo() nodeInfoDao.putAll(nodes)
setMyNodeInfo(mi) // set MyNodeInfo first
putAll(nodes)
}
} }
suspend fun deleteNode(num: Int) = withContext(Dispatchers.IO) { suspend fun deleteNode(num: Int) = withContext(Dispatchers.IO) {

Wyświetl plik

@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -50,17 +49,15 @@ class RadioConfigRepository @Inject constructor(
/** /**
* Flow representing the [MyNodeInfo] database. * Flow representing the [MyNodeInfo] database.
*/ */
fun myNodeInfoFlow(): Flow<MyNodeInfo?> = nodeDB.myNodeInfoFlow()
suspend fun getMyNodeInfo(): MyNodeInfo? = myNodeInfoFlow().firstOrNull()
val myNodeInfo: StateFlow<MyNodeInfo?> get() = nodeDB.myNodeInfo val myNodeInfo: StateFlow<MyNodeInfo?> get() = nodeDB.myNodeInfo
val nodeDBbyNum: StateFlow<Map<Int, NodeInfo>> get() = nodeDB.nodeDBbyNum
/** /**
* Flow representing the [NodeInfo] database. * Flow representing the [NodeInfo] database.
*/ */
suspend fun getNodes(): List<NodeInfo>? = nodeDB.getNodes().firstOrNull() val nodeDBbyNum: StateFlow<Map<Int, NodeInfo>> get() = nodeDB.nodeDBbyNum
suspend fun upsert(node: NodeInfo) = nodeDB.upsert(node) suspend fun upsert(node: NodeInfo) = nodeDB.upsert(node)
suspend fun clearNodeDB() = nodeDB.clearNodeDB()
suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) { suspend fun installNodeDB(mi: MyNodeInfo, nodes: List<NodeInfo>) {
nodeDB.installNodeDB(mi, nodes) nodeDB.installNodeDB(mi, nodes)
} }

Wyświetl plik

@ -46,7 +46,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.ConcurrentLinkedQueue
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException import java.util.concurrent.TimeoutException
@ -263,8 +262,6 @@ class MeshService : Service(), Logging {
radioConfigRepository.channelSetFlow.onEach { channelSet = it } radioConfigRepository.channelSetFlow.onEach { channelSet = it }
.launchIn(serviceScope) .launchIn(serviceScope)
loadSettings() // Load our last known node DB
// the rest of our init will happen once we are in radioConnection.onServiceConnected // the rest of our init will happen once we are in radioConnection.onServiceConnected
} }
@ -326,42 +323,16 @@ class MeshService : Service(), Logging {
/// BEGINNING OF MODEL - FIXME, move elsewhere /// BEGINNING OF MODEL - FIXME, move elsewhere
/// ///
private fun installNewNodeDB(ni: MyNodeInfo, nodes: List<NodeInfo>) {
discardNodeDB() // Get rid of any old state
myNodeInfo = ni
// put our node array into our two different map representations
nodeDBbyNodeNum.putAll(nodes.map { it.num to it })
}
private fun loadSettings() {
try {
serviceScope.handledLaunch {
val myInfo = radioConfigRepository.getMyNodeInfo()
val nodeDB = radioConfigRepository.getNodes()
if (myInfo != null && nodeDB != null) installNewNodeDB(myInfo, nodeDB)
// Note: we do not haveNodeDB = true because that means we've got a valid db from a real device (rather than this possibly stale hint)
}
} catch (ex: Exception) {
errormsg("Ignoring error loading saved state for service: ${ex.message}")
}
}
/** /**
* discard entire node db & message state - used when downloading a new db from the device * discard entire node db - used before downloading a new db from the device
*/ */
private fun discardNodeDB() { private fun discardNodeDB() = serviceScope.handledLaunch {
debug("Discarding NodeDB") debug("Discarding NodeDB")
myNodeInfo = null radioConfigRepository.clearNodeDB()
nodeDBbyNodeNum.clear()
haveNodeDB = false haveNodeDB = false
} }
var myNodeInfo: MyNodeInfo? = null val myNodeInfo: MyNodeInfo? get() = radioConfigRepository.myNodeInfo.value
private val configTotal by lazy { ConfigProtos.Config.getDescriptor().fields.size } private val configTotal by lazy { ConfigProtos.Config.getDescriptor().fields.size }
private val moduleTotal by lazy { ModuleConfigProtos.ModuleConfig.getDescriptor().fields.size } private val moduleTotal by lazy { ModuleConfigProtos.ModuleConfig.getDescriptor().fields.size }
@ -376,7 +347,7 @@ class MeshService : Service(), Logging {
private var haveNodeDB = false private var haveNodeDB = false
// The database of active nodes, index is the node number // The database of active nodes, index is the node number
private val nodeDBbyNodeNum = ConcurrentHashMap<Int, NodeInfo>() private val nodeDBbyNodeNum get() = radioConfigRepository.nodeDBbyNum.value
// The database of active nodes, index is the node user ID string // The database of active nodes, index is the node user ID string
// NOTE: some NodeInfos might be in only nodeDBbyNodeNum (because we don't yet know an ID). // NOTE: some NodeInfos might be in only nodeDBbyNodeNum (because we don't yet know an ID).
@ -402,15 +373,15 @@ class MeshService : Service(), Logging {
else nodeDBbyNodeNum[n]?.user?.id ?: DataPacket.nodeNumToDefaultId(n) else nodeDBbyNodeNum[n]?.user?.id ?: DataPacket.nodeNumToDefaultId(n)
// given a nodeNum, return a db entry - creating if necessary // given a nodeNum, return a db entry - creating if necessary
private fun getOrCreateNodeInfo(n: Int) = nodeDBbyNodeNum.getOrPut(n) { private fun getOrCreateNodeInfo(n: Int) = nodeDBbyNodeNum[n] ?: NodeInfo(
val defaultUser = MeshUser( n,
MeshUser(
id = DataPacket.nodeNumToDefaultId(n), id = DataPacket.nodeNumToDefaultId(n),
longName = getString(R.string.unknown_username), longName = getString(R.string.unknown_username),
shortName = getString(R.string.unknown_node_short_name), shortName = getString(R.string.unknown_node_short_name),
hwModel = MeshProtos.HardwareModel.UNSET, hwModel = MeshProtos.HardwareModel.UNSET,
) )
NodeInfo(n, defaultUser) )
}
private val hexIdRegex = """\!([0-9A-Fa-f]+)""".toRegex() private val hexIdRegex = """\!([0-9A-Fa-f]+)""".toRegex()
private val rangeTestRegex = Regex("seq (\\d{1,10})") private val rangeTestRegex = Regex("seq (\\d{1,10})")
@ -780,14 +751,7 @@ class MeshService : Service(), Logging {
/// Update our DB of users based on someone sending out a User subpacket /// Update our DB of users based on someone sending out a User subpacket
private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User, channel: Int = 0) { private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User, channel: Int = 0) {
updateNodeInfo(fromNum) { updateNodeInfo(fromNum) {
val oldId = it.user?.id.orEmpty() it.user = MeshUser(p)
it.user = MeshUser(
p.id.ifEmpty { oldId }, // If the new update doesn't contain an ID keep our old value
p.longName,
p.shortName,
p.hwModel,
p.isLicensed
)
it.channel = channel it.channel = channel
} }
} }
@ -1313,32 +1277,15 @@ class MeshService : Service(), Logging {
radioConfigRepository.setStatusMessage("Channels (${ch.index + 1} / $maxChannels)") radioConfigRepository.setStatusMessage("Channels (${ch.index + 1} / $maxChannels)")
} }
/** private fun MeshProtos.NodeInfo.toEntity() = NodeInfo(
* Convert a protobuf NodeInfo into our model objects and update our node DB num = num,
*/ user = MeshUser(user.copy { if (viaMqtt) longName = "$longName (MQTT)" }),
private fun installNodeInfo(info: MeshProtos.NodeInfo) { position = Position(position),
// Just replace/add any entry lastHeard = lastHeard,
updateNodeInfo(info.num) { deviceMetrics = DeviceMetrics(deviceMetrics),
if (info.hasUser()) { channel = channel,
it.user = MeshUser(info.user.copy { if (info.viaMqtt) longName = "$longName (MQTT)" }) hopsAway = hopsAway,
} )
if (info.hasPosition()) {
// For the local node, it might not be able to update its times because it doesn't have a valid GPS reading yet
// so if the info is for _our_ node we always assume time is current
it.position = Position(info.position)
}
it.lastHeard = info.lastHeard
if (info.hasDeviceMetrics()) {
it.deviceMetrics = DeviceMetrics(info.deviceMetrics)
}
it.channel = info.channel
it.hopsAway = info.hopsAway
}
}
private fun handleNodeInfo(info: MeshProtos.NodeInfo) { private fun handleNodeInfo(info: MeshProtos.NodeInfo) {
debug("Received nodeinfo num=${info.num}, hasUser=${info.hasUser()}, hasPosition=${info.hasPosition()}, hasDeviceMetrics=${info.hasDeviceMetrics()}") debug("Received nodeinfo num=${info.num}, hasUser=${info.hasUser()}, hasPosition=${info.hasPosition()}, hasDeviceMetrics=${info.hasDeviceMetrics()}")
@ -1497,7 +1444,7 @@ class MeshService : Service(), Logging {
reportConnection() reportConnection()
} }
private fun handleConfigComplete(configCompleteId: Int) { private fun handleConfigComplete(configCompleteId: Int) = serviceScope.handledLaunch {
if (configCompleteId == configNonce) { if (configCompleteId == configNonce) {
val packetToSave = MeshLog( val packetToSave = MeshLog(
@ -1512,19 +1459,12 @@ class MeshService : Service(), Logging {
if (newMyNodeInfo == null || newNodes.isEmpty()) { if (newMyNodeInfo == null || newNodes.isEmpty()) {
errormsg("Did not receive a valid config") errormsg("Did not receive a valid config")
} else { } else {
discardNodeDB()
debug("Installing new node DB") debug("Installing new node DB")
myNodeInfo = newMyNodeInfo radioConfigRepository.installNodeDB(newMyNodeInfo!!, newNodes.map { it.toEntity() })
newNodes.forEach(::installNodeInfo)
newNodes.clear() // Just to save RAM ;-) newNodes.clear() // Just to save RAM ;-)
haveNodeDB = true // we now have nodes from real hardware haveNodeDB = true // we now have nodes from real hardware
serviceScope.handledLaunch {
radioConfigRepository.installNodeDB(myNodeInfo!!, nodeDBbyID.values.toList())
}
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket {
setTimeOnly = currentSecond() setTimeOnly = currentSecond()
}) })