From c30ee2f55f9d7057dcdf793010d3f6571fa3d514 Mon Sep 17 00:00:00 2001 From: Andre K Date: Wed, 2 Oct 2024 06:18:30 -0300 Subject: [PATCH] refactor: replace `MyNodeInfo` with `MyNodeEntity` (#1277) --- .../13.json | 458 ++++++++++++++++++ .../com/geeksville/mesh/NodeInfoDaoTest.kt | 6 +- .../java/com/geeksville/mesh/PacketDaoTest.kt | 6 +- .../java/com/geeksville/mesh/MyNodeInfo.kt | 9 - .../main/java/com/geeksville/mesh/NodeInfo.kt | 69 --- .../mesh/database/MeshtasticDatabase.kt | 17 +- .../mesh/database/dao/NodeInfoDao.kt | 16 +- .../geeksville/mesh/database/dao/PacketDao.kt | 22 +- .../mesh/database/entity/MyNodeEntity.kt | 39 ++ .../java/com/geeksville/mesh/model/NodeDB.kt | 10 +- .../mesh/model/RadioConfigViewModel.kt | 4 +- .../java/com/geeksville/mesh/model/UIState.kt | 3 +- .../datastore/RadioConfigRepository.kt | 8 +- .../geeksville/mesh/service/MeshService.kt | 12 +- 14 files changed, 550 insertions(+), 129 deletions(-) create mode 100644 app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/13.json create mode 100644 app/src/main/java/com/geeksville/mesh/database/entity/MyNodeEntity.kt diff --git a/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/13.json b/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/13.json new file mode 100644 index 00000000..1cc689ff --- /dev/null +++ b/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/13.json @@ -0,0 +1,458 @@ +{ + "formatVersion": 1, + "database": { + "version": 13, + "identityHash": "23eb673bdcd4e1af18a662f0b49bfebf", + "entities": [ + { + "tableName": "my_node", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`myNodeNum` INTEGER NOT NULL, `model` TEXT, `firmwareVersion` TEXT, `couldUpdate` INTEGER NOT NULL, `shouldUpdate` INTEGER NOT NULL, `currentPacketId` INTEGER NOT NULL, `messageTimeoutMsec` INTEGER NOT NULL, `minAppVersion` INTEGER NOT NULL, `maxChannels` INTEGER NOT NULL, `hasWifi` INTEGER NOT NULL, PRIMARY KEY(`myNodeNum`))", + "fields": [ + { + "fieldPath": "myNodeNum", + "columnName": "myNodeNum", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "model", + "columnName": "model", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "firmwareVersion", + "columnName": "firmwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "couldUpdate", + "columnName": "couldUpdate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "shouldUpdate", + "columnName": "shouldUpdate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "currentPacketId", + "columnName": "currentPacketId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageTimeoutMsec", + "columnName": "messageTimeoutMsec", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minAppVersion", + "columnName": "minAppVersion", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "maxChannels", + "columnName": "maxChannels", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasWifi", + "columnName": "hasWifi", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "myNodeNum" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "nodes", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`num` INTEGER NOT NULL, `user` BLOB NOT NULL, `long_name` TEXT, `short_name` TEXT, `position` BLOB NOT NULL, `latitude` REAL NOT NULL, `longitude` REAL NOT NULL, `snr` REAL NOT NULL, `rssi` INTEGER NOT NULL, `last_heard` INTEGER NOT NULL, `device_metrics` BLOB NOT NULL, `channel` INTEGER NOT NULL, `via_mqtt` INTEGER NOT NULL, `hops_away` INTEGER NOT NULL, `is_favorite` INTEGER NOT NULL, `environment_metrics` BLOB NOT NULL, `power_metrics` BLOB NOT NULL, `paxcounter` BLOB NOT NULL, PRIMARY KEY(`num`))", + "fields": [ + { + "fieldPath": "num", + "columnName": "num", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "user", + "columnName": "user", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "longName", + "columnName": "long_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "shortName", + "columnName": "short_name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "snr", + "columnName": "snr", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rssi", + "columnName": "rssi", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastHeard", + "columnName": "last_heard", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceTelemetry", + "columnName": "device_metrics", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "channel", + "columnName": "channel", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "viaMqtt", + "columnName": "via_mqtt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hopsAway", + "columnName": "hops_away", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFavorite", + "columnName": "is_favorite", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "environmentTelemetry", + "columnName": "environment_metrics", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "powerTelemetry", + "columnName": "power_metrics", + "affinity": "BLOB", + "notNull": true + }, + { + "fieldPath": "paxcounter", + "columnName": "paxcounter", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "num" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "packet", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `myNodeNum` INTEGER NOT NULL DEFAULT 0, `port_num` INTEGER NOT NULL, `contact_key` TEXT NOT NULL, `received_time` INTEGER NOT NULL, `read` INTEGER NOT NULL DEFAULT 1, `data` TEXT NOT NULL, `packet_id` INTEGER NOT NULL DEFAULT 0, `routing_error` INTEGER NOT NULL DEFAULT -1)", + "fields": [ + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "myNodeNum", + "columnName": "myNodeNum", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "port_num", + "columnName": "port_num", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "contact_key", + "columnName": "contact_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "received_time", + "columnName": "received_time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "read", + "columnName": "read", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packetId", + "columnName": "packet_id", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "routingError", + "columnName": "routing_error", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uuid" + ] + }, + "indices": [ + { + "name": "index_packet_myNodeNum", + "unique": false, + "columnNames": [ + "myNodeNum" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_packet_myNodeNum` ON `${TABLE_NAME}` (`myNodeNum`)" + }, + { + "name": "index_packet_port_num", + "unique": false, + "columnNames": [ + "port_num" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_packet_port_num` ON `${TABLE_NAME}` (`port_num`)" + }, + { + "name": "index_packet_contact_key", + "unique": false, + "columnNames": [ + "contact_key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_packet_contact_key` ON `${TABLE_NAME}` (`contact_key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "contact_settings", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`contact_key` TEXT NOT NULL, `muteUntil` INTEGER NOT NULL, PRIMARY KEY(`contact_key`))", + "fields": [ + { + "fieldPath": "contact_key", + "columnName": "contact_key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "muteUntil", + "columnName": "muteUntil", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "contact_key" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "log", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` TEXT NOT NULL, `type` TEXT NOT NULL, `received_date` INTEGER NOT NULL, `message` TEXT NOT NULL, `from_num` INTEGER NOT NULL DEFAULT 0, `port_num` INTEGER NOT NULL DEFAULT 0, `from_radio` BLOB NOT NULL DEFAULT x'', PRIMARY KEY(`uuid`))", + "fields": [ + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message_type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "received_date", + "columnName": "received_date", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "raw_message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fromNum", + "columnName": "from_num", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "portNum", + "columnName": "port_num", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "fromRadio", + "columnName": "from_radio", + "affinity": "BLOB", + "notNull": true, + "defaultValue": "x''" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uuid" + ] + }, + "indices": [ + { + "name": "index_log_from_num", + "unique": false, + "columnNames": [ + "from_num" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_log_from_num` ON `${TABLE_NAME}` (`from_num`)" + }, + { + "name": "index_log_port_num", + "unique": false, + "columnNames": [ + "port_num" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_log_port_num` ON `${TABLE_NAME}` (`port_num`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "quick_chat", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `message` TEXT NOT NULL, `mode` TEXT NOT NULL, `position` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "message", + "columnName": "message", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mode", + "columnName": "mode", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "position", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uuid" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '23eb673bdcd4e1af18a662f0b49bfebf')" + ] + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/geeksville/mesh/NodeInfoDaoTest.kt b/app/src/androidTest/java/com/geeksville/mesh/NodeInfoDaoTest.kt index 059206b9..03b740d8 100644 --- a/app/src/androidTest/java/com/geeksville/mesh/NodeInfoDaoTest.kt +++ b/app/src/androidTest/java/com/geeksville/mesh/NodeInfoDaoTest.kt @@ -5,6 +5,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.geeksville.mesh.database.MeshtasticDatabase import com.geeksville.mesh.database.dao.NodeInfoDao +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.model.NodeSortOption import kotlinx.coroutines.flow.first @@ -35,9 +36,8 @@ class NodeInfoDaoTest { latitude = 30.267153, longitude = -97.743057 // Austin ) - private val myNodeInfo: MyNodeInfo = MyNodeInfo( + private val myNodeInfo: MyNodeEntity = MyNodeEntity( myNodeNum = ourNode.num, - hasGPS = false, model = null, firmwareVersion = null, couldUpdate = false, @@ -47,8 +47,6 @@ class NodeInfoDaoTest { minAppVersion = 1, maxChannels = 8, hasWifi = false, - channelUtilization = 0f, - airUtilTx = 0f, ) private val testPositions = arrayOf( diff --git a/app/src/androidTest/java/com/geeksville/mesh/PacketDaoTest.kt b/app/src/androidTest/java/com/geeksville/mesh/PacketDaoTest.kt index 1395115b..6f2240d0 100644 --- a/app/src/androidTest/java/com/geeksville/mesh/PacketDaoTest.kt +++ b/app/src/androidTest/java/com/geeksville/mesh/PacketDaoTest.kt @@ -6,6 +6,7 @@ import androidx.test.platform.app.InstrumentationRegistry import com.geeksville.mesh.database.MeshtasticDatabase import com.geeksville.mesh.database.dao.NodeInfoDao import com.geeksville.mesh.database.dao.PacketDao +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.Packet import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking @@ -22,9 +23,8 @@ class PacketDaoTest { private lateinit var nodeInfoDao: NodeInfoDao private lateinit var packetDao: PacketDao - private val myNodeInfo: MyNodeInfo = MyNodeInfo( + private val myNodeInfo: MyNodeEntity = MyNodeEntity( myNodeNum = 42424242, - hasGPS = false, model = null, firmwareVersion = null, couldUpdate = false, @@ -34,8 +34,6 @@ class PacketDaoTest { minAppVersion = 1, maxChannels = 8, hasWifi = false, - channelUtilization = 0f, - airUtilTx = 0f, ) private val myNodeNum: Int get() = myNodeInfo.myNodeNum diff --git a/app/src/main/java/com/geeksville/mesh/MyNodeInfo.kt b/app/src/main/java/com/geeksville/mesh/MyNodeInfo.kt index d675d72a..66ee73c9 100644 --- a/app/src/main/java/com/geeksville/mesh/MyNodeInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/MyNodeInfo.kt @@ -1,20 +1,11 @@ package com.geeksville.mesh import android.os.Parcelable -import androidx.room.Entity -import androidx.room.PrimaryKey import kotlinx.parcelize.Parcelize -/** - * Room [Entity] and [PrimaryKey] annotations and imports can be removed when only using the API. - * For details check the AIDL interface in [com.geeksville.mesh.IMeshService] - */ - // MyNodeInfo sent via special protobuf from radio @Parcelize -@Entity(tableName = "MyNodeInfo") data class MyNodeInfo( - @PrimaryKey(autoGenerate = false) val myNodeNum: Int, val hasGPS: Boolean, val model: String?, diff --git a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt index 7a9e1864..670053ed 100644 --- a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt @@ -2,22 +2,12 @@ package com.geeksville.mesh import android.graphics.Color import android.os.Parcelable -import androidx.room.ColumnInfo -import androidx.room.Embedded -import androidx.room.Entity -import androidx.room.PrimaryKey import com.geeksville.mesh.util.GPSFormat import com.geeksville.mesh.util.bearing import com.geeksville.mesh.util.latLongToMeter import com.geeksville.mesh.util.anonymize import kotlinx.parcelize.Parcelize -/** - * Room [Embedded], [Entity] and [PrimaryKey] annotations and imports, as well as any protobuf - * reference [MeshProtos], [TelemetryProtos], [ConfigProtos] can be removed when only using the API. - * For details check the AIDL interface in [com.geeksville.mesh.IMeshService] - */ - // // model objects that directly map to the corresponding protobufs // @@ -29,7 +19,6 @@ data class MeshUser( val shortName: String, val hwModel: MeshProtos.HardwareModel, val isLicensed: Boolean = false, - @ColumnInfo(name = "role", defaultValue = "0") val role: Int = 0, ) : Parcelable { @@ -53,16 +42,6 @@ data class MeshUser( p.roleValue ) - fun toProto(): MeshProtos.User = - MeshProtos.User.newBuilder() - .setId(id) - .setLongName(longName) - .setShortName(shortName) - .setHwModel(hwModel) - .setIsLicensed(isLicensed) - .setRoleValue(role) - .build() - /** a string version of the hardware model, converted into pretty lowercase and changing _ to -, and p to dot * or null if unset * */ @@ -172,67 +151,19 @@ data class EnvironmentMetrics( companion object { fun currentTime() = (System.currentTimeMillis() / 1000).toInt() } - - /** Create our model object from a protobuf. - */ - constructor(t: TelemetryProtos.EnvironmentMetrics, telemetryTime: Int = currentTime()) : this( - telemetryTime, - t.temperature, - t.relativeHumidity, - t.barometricPressure, - t.gasResistance, - t.voltage, - t.current, - t.iaq, - ) - - fun getDisplayString(inFahrenheit: Boolean = false): String { - val temp = if (temperature != 0f) { - if (inFahrenheit) { - val fahrenheit = temperature * 1.8F + 32 - String.format("%.1f°F", fahrenheit) - } else { - String.format("%.1f°C", temperature) - } - } else null - val humidity = if (relativeHumidity != 0f) String.format("%.0f%%", relativeHumidity) else null - val pressure = if (barometricPressure != 0f) String.format("%.1fhPa", barometricPressure) else null - val gas = if (gasResistance != 0f) String.format("%.0fMΩ", gasResistance) else null - val voltage = if (voltage != 0f) String.format("%.2fV", voltage) else null - val current = if (current != 0f) String.format("%.1fmA", current) else null - val iaq = if (iaq != 0) "IAQ: $iaq" else null - - return listOfNotNull( - temp, - humidity, - pressure, - gas, - voltage, - current, - iaq, - ).joinToString(" ") - } - } @Parcelize -@Entity(tableName = "NodeInfo") data class NodeInfo( - @PrimaryKey(autoGenerate = false) val num: Int, // This is immutable, and used as a key - @Embedded(prefix = "user_") var user: MeshUser? = null, - @Embedded(prefix = "position_") var position: Position? = null, var snr: Float = Float.MAX_VALUE, var rssi: Int = Int.MAX_VALUE, var lastHeard: Int = 0, // the last time we've seen this node in secs since 1970 - @Embedded(prefix = "devMetrics_") var deviceMetrics: DeviceMetrics? = null, var channel: Int = 0, - @Embedded(prefix = "envMetrics_") var environmentMetrics: EnvironmentMetrics? = null, - @ColumnInfo(name = "hopsAway", defaultValue = "0") var hopsAway: Int = 0 ) : Parcelable { diff --git a/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt b/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt index cb44d228..fd7d8546 100644 --- a/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt +++ b/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt @@ -3,25 +3,25 @@ package com.geeksville.mesh.database import android.content.Context import androidx.room.AutoMigration import androidx.room.Database +import androidx.room.DeleteTable import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.TypeConverters -import com.geeksville.mesh.MyNodeInfo -import com.geeksville.mesh.NodeInfo +import androidx.room.migration.AutoMigrationSpec import com.geeksville.mesh.database.dao.PacketDao import com.geeksville.mesh.database.dao.MeshLogDao import com.geeksville.mesh.database.dao.NodeInfoDao import com.geeksville.mesh.database.dao.QuickChatActionDao import com.geeksville.mesh.database.entity.ContactSettings import com.geeksville.mesh.database.entity.MeshLog +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.Packet import com.geeksville.mesh.database.entity.QuickChatAction @Database( entities = [ - MyNodeInfo::class, - NodeInfo::class, + MyNodeEntity::class, NodeEntity::class, Packet::class, ContactSettings::class, @@ -38,8 +38,9 @@ import com.geeksville.mesh.database.entity.QuickChatAction AutoMigration (from = 9, to = 10), AutoMigration (from = 10, to = 11), AutoMigration (from = 11, to = 12), + AutoMigration(from = 12, to = 13, spec = AutoMigration12to13::class), ], - version = 12, + version = 13, exportSchema = true, ) @TypeConverters(Converters::class) @@ -62,3 +63,9 @@ abstract class MeshtasticDatabase : RoomDatabase() { } } } + +@DeleteTable.Entries( + DeleteTable(tableName = "NodeInfo"), + DeleteTable(tableName = "MyNodeInfo") +) +class AutoMigration12to13 : AutoMigrationSpec diff --git a/app/src/main/java/com/geeksville/mesh/database/dao/NodeInfoDao.kt b/app/src/main/java/com/geeksville/mesh/database/dao/NodeInfoDao.kt index 77f01d89..7fe9469e 100644 --- a/app/src/main/java/com/geeksville/mesh/database/dao/NodeInfoDao.kt +++ b/app/src/main/java/com/geeksville/mesh/database/dao/NodeInfoDao.kt @@ -6,27 +6,27 @@ import androidx.room.MapColumn import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Upsert -import com.geeksville.mesh.MyNodeInfo +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import kotlinx.coroutines.flow.Flow @Dao interface NodeInfoDao { - @Query("SELECT * FROM MyNodeInfo") - fun getMyNodeInfo(): Flow + @Query("SELECT * FROM my_node") + fun getMyNodeInfo(): Flow @Insert(onConflict = OnConflictStrategy.REPLACE) - fun setMyNodeInfo(myInfo: MyNodeInfo) + fun setMyNodeInfo(myInfo: MyNodeEntity) - @Query("DELETE FROM MyNodeInfo") + @Query("DELETE FROM my_node") fun clearMyNodeInfo() @Query( """ SELECT * FROM nodes ORDER BY CASE - WHEN num = (SELECT myNodeNum FROM MyNodeInfo LIMIT 1) THEN 0 + WHEN num = (SELECT myNodeNum FROM my_node LIMIT 1) THEN 0 ELSE 1 END, last_heard DESC @@ -39,7 +39,7 @@ interface NodeInfoDao { WITH OurNode AS ( SELECT latitude, longitude FROM nodes - WHERE num = (SELECT myNodeNum FROM MyNodeInfo LIMIT 1) + WHERE num = (SELECT myNodeNum FROM my_node LIMIT 1) ) SELECT * FROM nodes WHERE (:includeUnknown = 1 OR short_name IS NOT NULL) @@ -47,7 +47,7 @@ interface NodeInfoDao { OR (long_name LIKE '%' || :filter || '%' OR short_name LIKE '%' || :filter || '%')) ORDER BY CASE - WHEN num = (SELECT myNodeNum FROM MyNodeInfo LIMIT 1) THEN 0 + WHEN num = (SELECT myNodeNum FROM my_node LIMIT 1) THEN 0 ELSE 1 END, CASE diff --git a/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt b/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt index eae63234..50c79023 100644 --- a/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt +++ b/app/src/main/java/com/geeksville/mesh/database/dao/PacketDao.kt @@ -19,7 +19,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = :portNum ORDER BY received_time ASC """ @@ -29,7 +29,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 1 ORDER BY received_time DESC """ @@ -39,7 +39,7 @@ interface PacketDao { @Query( """ SELECT COUNT(*) FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 1 AND contact_key = :contact """ ) @@ -48,7 +48,7 @@ interface PacketDao { @Query( """ SELECT COUNT(*) FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 1 AND contact_key = :contact AND read = 0 """ ) @@ -58,7 +58,7 @@ interface PacketDao { """ UPDATE packet SET read = 1 - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 1 AND contact_key = :contact AND read = 0 AND received_time <= :timestamp """ ) @@ -70,7 +70,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 1 AND contact_key = :contact ORDER BY received_time DESC """ @@ -80,7 +80,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND data = :data """ ) @@ -92,7 +92,7 @@ interface PacketDao { @Query( """ DELETE FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND contact_key IN (:contactList) """ ) @@ -124,7 +124,7 @@ interface PacketDao { @Query( """ SELECT data FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) ORDER BY received_time ASC """ ) @@ -133,7 +133,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND packet_id = :requestId ORDER BY received_time DESC """ @@ -147,7 +147,7 @@ interface PacketDao { @Query( """ SELECT * FROM packet - WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM MyNodeInfo)) + WHERE (myNodeNum = 0 OR myNodeNum = (SELECT myNodeNum FROM my_node)) AND port_num = 8 ORDER BY received_time ASC """ diff --git a/app/src/main/java/com/geeksville/mesh/database/entity/MyNodeEntity.kt b/app/src/main/java/com/geeksville/mesh/database/entity/MyNodeEntity.kt new file mode 100644 index 00000000..b14ccc30 --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/database/entity/MyNodeEntity.kt @@ -0,0 +1,39 @@ +package com.geeksville.mesh.database.entity + +import androidx.room.Entity +import androidx.room.PrimaryKey +import com.geeksville.mesh.MyNodeInfo + +@Entity(tableName = "my_node") +data class MyNodeEntity( + @PrimaryKey(autoGenerate = false) + val myNodeNum: Int, + val model: String?, + val firmwareVersion: String?, + val couldUpdate: Boolean, // this application contains a software load we _could_ install if you want + val shouldUpdate: Boolean, // this device has old firmware + val currentPacketId: Long, + val messageTimeoutMsec: Int, + val minAppVersion: Int, + val maxChannels: Int, + val hasWifi: Boolean, +) { + /** A human readable description of the software/hardware version */ + val firmwareString: String get() = "$model $firmwareVersion" + + fun toMyNodeInfo() = MyNodeInfo( + myNodeNum = myNodeNum, + hasGPS = false, + model = model, + firmwareVersion = firmwareVersion, + couldUpdate = couldUpdate, + shouldUpdate = shouldUpdate, + currentPacketId = currentPacketId, + messageTimeoutMsec = messageTimeoutMsec, + minAppVersion = minAppVersion, + maxChannels = maxChannels, + hasWifi = hasWifi, + channelUtilization = 0f, + airUtilTx = 0f, + ) +} 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 6f9f3e32..871b356b 100644 --- a/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt +++ b/app/src/main/java/com/geeksville/mesh/model/NodeDB.kt @@ -2,8 +2,8 @@ package com.geeksville.mesh.model import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope -import com.geeksville.mesh.MyNodeInfo import com.geeksville.mesh.database.dao.NodeInfoDao +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow @@ -20,8 +20,8 @@ class NodeDB @Inject constructor( private val nodeInfoDao: NodeInfoDao, ) { // hardware info about our local device (can be null) - private val _myNodeInfo = MutableStateFlow(null) - val myNodeInfo: StateFlow get() = _myNodeInfo + private val _myNodeInfo = MutableStateFlow(null) + val myNodeInfo: StateFlow get() = _myNodeInfo // our node info private val _ourNodeInfo = MutableStateFlow(null) @@ -65,9 +65,9 @@ class NodeDB @Inject constructor( nodeInfoDao.upsert(node) } - suspend fun installNodeDB(mi: MyNodeInfo, nodes: List) = withContext(Dispatchers.IO) { + suspend fun installNodeDB(mi: MyNodeEntity, nodes: List) = withContext(Dispatchers.IO) { nodeInfoDao.clearMyNodeInfo() - nodeInfoDao.setMyNodeInfo(mi) // set MyNodeInfo first + nodeInfoDao.setMyNodeInfo(mi) // set MyNodeEntity first nodeInfoDao.clearNodeInfo() nodeInfoDao.putAll(nodes) } diff --git a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt index 726bf919..140e76ee 100644 --- a/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt +++ b/app/src/main/java/com/geeksville/mesh/model/RadioConfigViewModel.kt @@ -12,12 +12,12 @@ import com.geeksville.mesh.ConfigProtos import com.geeksville.mesh.IMeshService import com.geeksville.mesh.MeshProtos import com.geeksville.mesh.ModuleConfigProtos -import com.geeksville.mesh.MyNodeInfo import com.geeksville.mesh.Portnums import com.geeksville.mesh.Position import com.geeksville.mesh.R import com.geeksville.mesh.android.Logging import com.geeksville.mesh.config +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.deviceProfile import com.geeksville.mesh.moduleConfig @@ -102,7 +102,7 @@ class RadioConfigViewModel @Inject constructor( debug("RadioConfigViewModel created") } - private val myNodeInfo: StateFlow get() = radioConfigRepository.myNodeInfo + private val myNodeInfo: StateFlow get() = radioConfigRepository.myNodeInfo val myNodeNum get() = myNodeInfo.value?.myNodeNum val maxChannels get() = myNodeInfo.value?.maxChannels ?: 8 diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt index c0573460..50bebf72 100644 --- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt +++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt @@ -21,6 +21,7 @@ import com.geeksville.mesh.android.Logging import com.geeksville.mesh.database.MeshLogRepository import com.geeksville.mesh.database.PacketRepository import com.geeksville.mesh.database.QuickChatActionRepository +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.Packet import com.geeksville.mesh.database.entity.QuickChatAction @@ -238,7 +239,7 @@ class UIViewModel @Inject constructor( ) // hardware info about our local device (can be null) - val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo + val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo val ourNodeInfo: StateFlow get() = nodeDB.ourNodeInfo val nodesWithPosition get() = nodeDB.nodeDBbyNum.value.values.filter { it.validPosition != null } 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 72b2672a..0ed2ed12 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 @@ -10,7 +10,7 @@ import com.geeksville.mesh.LocalOnlyProtos.LocalConfig import com.geeksville.mesh.LocalOnlyProtos.LocalModuleConfig import com.geeksville.mesh.MeshProtos.MeshPacket import com.geeksville.mesh.ModuleConfigProtos.ModuleConfig -import com.geeksville.mesh.MyNodeInfo +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.deviceProfile import com.geeksville.mesh.model.NodeDB @@ -47,9 +47,9 @@ class RadioConfigRepository @Inject constructor( val myId: StateFlow get() = nodeDB.myId /** - * Flow representing the [MyNodeInfo] database. + * Flow representing the [MyNodeEntity] database. */ - val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo + val myNodeInfo: StateFlow get() = nodeDB.myNodeInfo /** * Flow representing the [NodeEntity] database. @@ -57,7 +57,7 @@ class RadioConfigRepository @Inject constructor( val nodeDBbyNum: StateFlow> get() = nodeDB.nodeDBbyNum suspend fun upsert(node: NodeEntity) = nodeDB.upsert(node) - suspend fun installNodeDB(mi: MyNodeInfo, nodes: List) { + suspend fun installNodeDB(mi: MyNodeEntity, 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 a3a0ef0a..78140c42 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -22,6 +22,7 @@ import com.geeksville.mesh.android.hasLocationPermission import com.geeksville.mesh.database.MeshLogRepository import com.geeksville.mesh.database.PacketRepository import com.geeksville.mesh.database.entity.MeshLog +import com.geeksville.mesh.database.entity.MyNodeEntity import com.geeksville.mesh.database.entity.NodeEntity import com.geeksville.mesh.database.entity.Packet import com.geeksville.mesh.database.entity.toNodeInfo @@ -352,7 +353,7 @@ class MeshService : Service(), Logging { haveNodeDB = false } - private var myNodeInfo: MyNodeInfo? = null + private var myNodeInfo: MyNodeEntity? = null private val configTotal by lazy { ConfigProtos.Config.getDescriptor().fields.size } private val moduleTotal by lazy { ModuleConfigProtos.ModuleConfig.getDescriptor().fields.size } @@ -1316,7 +1317,7 @@ class MeshService : Service(), Logging { } /// A provisional MyNodeInfo that we will install if all of our node config downloads go okay - private var newMyNodeInfo: MyNodeInfo? = null + private var newMyNodeInfo: MyNodeEntity? = null /// provisional NodeInfos we will install if all goes well private val newNodes = mutableListOf() @@ -1436,9 +1437,8 @@ class MeshService : Service(), Logging { val myInfo = rawMyNodeInfo if (myInfo != null) { val mi = with(myInfo) { - MyNodeInfo( + MyNodeEntity( myNodeNum = myNodeNum, - hasGPS = false, model = rawDeviceMetadata?.hwModel?.let { hwModel -> if (hwModel == MeshProtos.HardwareModel.UNSET) null else hwModel.name.replace('_', '-').replace('p', '.').lowercase() @@ -1451,8 +1451,6 @@ class MeshService : Service(), Logging { minAppVersion = minAppVersion, maxChannels = 8, hasWifi = rawDeviceMetadata?.hasWifi ?: false, - channelUtilization = 0f, - airUtilTx = 0f, ) } newMyNodeInfo = mi @@ -1743,7 +1741,7 @@ class MeshService : Service(), Logging { // TODO reimplement this after we have a new firmware update mechanism } - override fun getMyNodeInfo(): MyNodeInfo? = this@MeshService.myNodeInfo + override fun getMyNodeInfo(): MyNodeInfo? = this@MeshService.myNodeInfo?.toMyNodeInfo() override fun getMyId() = toRemoteExceptions { myNodeID }