From 39a18e64189d5b9f9cee68a4d82c8581e2b6d44c Mon Sep 17 00:00:00 2001 From: andrekir Date: Fri, 13 Sep 2024 00:19:21 -0300 Subject: [PATCH] refactor: replace service local node db with Room NodeDB --- .../java/com/geeksville/mesh/model/NodeDB.kt | 14 +-- .../datastore/RadioConfigRepository.kt | 7 +- .../geeksville/mesh/service/MeshService.kt | 102 ++++-------------- 3 files changed, 31 insertions(+), 92 deletions(-) diff --git a/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt b/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt index 8c14a7c5d..5b418417c 100644 --- a/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt +++ b/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt @@ -70,13 +70,15 @@ class NodeDB @Inject constructor( nodeInfoDao.upsert(node) } + suspend fun clearNodeDB() = withContext(Dispatchers.IO) { + nodeInfoDao.clearNodeInfo() + nodeInfoDao.clearMyNodeInfo() + } + suspend fun installNodeDB(mi: MyNodeInfo, nodes: List) = withContext(Dispatchers.IO) { - nodeInfoDao.apply { - clearNodeInfo() - clearMyNodeInfo() - setMyNodeInfo(mi) // set MyNodeInfo first - putAll(nodes) - } + clearNodeDB() + nodeInfoDao.setMyNodeInfo(mi) // set MyNodeInfo first + nodeInfoDao.putAll(nodes) } suspend fun deleteNode(num: Int) = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt index 52ee5c6aa..b22ec9f50 100644 --- a/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt +++ b/app/src/main/java/com/geeksville/mesh/repository/datastore/RadioConfigRepository.kt @@ -22,7 +22,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.firstOrNull import javax.inject.Inject /** @@ -50,17 +49,15 @@ class RadioConfigRepository @Inject constructor( /** * Flow representing the [MyNodeInfo] database. */ - fun myNodeInfoFlow(): Flow = nodeDB.myNodeInfoFlow() - suspend fun getMyNodeInfo(): MyNodeInfo? = myNodeInfoFlow().firstOrNull() val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo - val nodeDBbyNum: StateFlow> get() = nodeDB.nodeDBbyNum /** * Flow representing the [NodeInfo] database. */ - suspend fun getNodes(): List? = nodeDB.getNodes().firstOrNull() + val nodeDBbyNum: StateFlow> get() = nodeDB.nodeDBbyNum suspend fun upsert(node: NodeInfo) = nodeDB.upsert(node) + suspend fun clearNodeDB() = nodeDB.clearNodeDB() suspend fun installNodeDB(mi: MyNodeInfo, nodes: List) { nodeDB.installNodeDB(mi, nodes) } diff --git a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt index 2eee96efd..98627a5da 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -46,7 +46,6 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withTimeoutOrNull import java.util.* -import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException @@ -263,8 +262,6 @@ class MeshService : Service(), Logging { radioConfigRepository.channelSetFlow.onEach { channelSet = it } .launchIn(serviceScope) - loadSettings() // Load our last known node DB - // 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 /// - private fun installNewNodeDB(ni: MyNodeInfo, nodes: List) { - - 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") - myNodeInfo = null - nodeDBbyNodeNum.clear() + radioConfigRepository.clearNodeDB() 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 moduleTotal by lazy { ModuleConfigProtos.ModuleConfig.getDescriptor().fields.size } @@ -376,7 +347,7 @@ class MeshService : Service(), Logging { private var haveNodeDB = false // The database of active nodes, index is the node number - private val nodeDBbyNodeNum = ConcurrentHashMap() + private val nodeDBbyNodeNum get() = radioConfigRepository.nodeDBbyNum.value // 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). @@ -402,15 +373,15 @@ class MeshService : Service(), Logging { else nodeDBbyNodeNum[n]?.user?.id ?: DataPacket.nodeNumToDefaultId(n) // given a nodeNum, return a db entry - creating if necessary - private fun getOrCreateNodeInfo(n: Int) = nodeDBbyNodeNum.getOrPut(n) { - val defaultUser = MeshUser( + private fun getOrCreateNodeInfo(n: Int) = nodeDBbyNodeNum[n] ?: NodeInfo( + n, + MeshUser( id = DataPacket.nodeNumToDefaultId(n), longName = getString(R.string.unknown_username), shortName = getString(R.string.unknown_node_short_name), hwModel = MeshProtos.HardwareModel.UNSET, ) - NodeInfo(n, defaultUser) - } + ) private val hexIdRegex = """\!([0-9A-Fa-f]+)""".toRegex() 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 private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User, channel: Int = 0) { updateNodeInfo(fromNum) { - val oldId = it.user?.id.orEmpty() - 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.user = MeshUser(p) it.channel = channel } } @@ -1313,32 +1277,15 @@ class MeshService : Service(), Logging { radioConfigRepository.setStatusMessage("Channels (${ch.index + 1} / $maxChannels)") } - /** - * Convert a protobuf NodeInfo into our model objects and update our node DB - */ - private fun installNodeInfo(info: MeshProtos.NodeInfo) { - // Just replace/add any entry - updateNodeInfo(info.num) { - if (info.hasUser()) { - it.user = MeshUser(info.user.copy { if (info.viaMqtt) longName = "$longName (MQTT)" }) - } - - 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 MeshProtos.NodeInfo.toEntity() = NodeInfo( + num = num, + user = MeshUser(user.copy { if (viaMqtt) longName = "$longName (MQTT)" }), + position = Position(position), + lastHeard = lastHeard, + deviceMetrics = DeviceMetrics(deviceMetrics), + channel = channel, + hopsAway = hopsAway, + ) private fun handleNodeInfo(info: MeshProtos.NodeInfo) { debug("Received nodeinfo num=${info.num}, hasUser=${info.hasUser()}, hasPosition=${info.hasPosition()}, hasDeviceMetrics=${info.hasDeviceMetrics()}") @@ -1497,7 +1444,7 @@ class MeshService : Service(), Logging { reportConnection() } - private fun handleConfigComplete(configCompleteId: Int) { + private fun handleConfigComplete(configCompleteId: Int) = serviceScope.handledLaunch { if (configCompleteId == configNonce) { val packetToSave = MeshLog( @@ -1512,19 +1459,12 @@ class MeshService : Service(), Logging { if (newMyNodeInfo == null || newNodes.isEmpty()) { errormsg("Did not receive a valid config") } else { - discardNodeDB() debug("Installing new node DB") - myNodeInfo = newMyNodeInfo - - newNodes.forEach(::installNodeInfo) + radioConfigRepository.installNodeDB(newMyNodeInfo!!, newNodes.map { it.toEntity() }) newNodes.clear() // Just to save RAM ;-) haveNodeDB = true // we now have nodes from real hardware - serviceScope.handledLaunch { - radioConfigRepository.installNodeDB(myNodeInfo!!, nodeDBbyID.values.toList()) - } - sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket { setTimeOnly = currentSecond() })