From 7883d72881fde2111cf3c6c33e4d55aee8c75c76 Mon Sep 17 00:00:00 2001 From: DayleDrinkwater Date: Thu, 11 Apr 2024 10:41:14 +0100 Subject: [PATCH 1/8] Initial Hops Away feature --- .idea/inspectionProfiles/Project_Default.xml | 4 + .../6.json | 433 ++++++++++++++++++ .../main/java/com/geeksville/mesh/NodeInfo.kt | 3 + .../mesh/database/MeshtasticDatabase.kt | 3 +- .../geeksville/mesh/service/MeshService.kt | 1 + .../java/com/geeksville/mesh/ui/SignalInfo.kt | 11 +- .../ui/preview/PreviewParameterProviders.kt | 9 +- 7 files changed, 456 insertions(+), 8 deletions(-) create mode 100644 app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/6.json diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 8a10375bd..c266be751 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,15 +2,19 @@ diff --git a/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/6.json b/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/6.json new file mode 100644 index 000000000..8c566b245 --- /dev/null +++ b/app/schemas/com.geeksville.mesh.database.MeshtasticDatabase/6.json @@ -0,0 +1,433 @@ +{ + "formatVersion": 1, + "database": { + "version": 6, + "identityHash": "101c86e2befadaec004cbc7b90f2e961", + "entities": [ + { + "tableName": "MyNodeInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`myNodeNum` INTEGER NOT NULL, `hasGPS` 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, `channelUtilization` REAL NOT NULL, `airUtilTx` REAL NOT NULL, PRIMARY KEY(`myNodeNum`))", + "fields": [ + { + "fieldPath": "myNodeNum", + "columnName": "myNodeNum", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasGPS", + "columnName": "hasGPS", + "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 + }, + { + "fieldPath": "channelUtilization", + "columnName": "channelUtilization", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "airUtilTx", + "columnName": "airUtilTx", + "affinity": "REAL", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "myNodeNum" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "NodeInfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`num` INTEGER NOT NULL, `snr` REAL NOT NULL, `rssi` INTEGER NOT NULL, `lastHeard` INTEGER NOT NULL, `hopsAway` INTEGER NOT NULL DEFAULT 0, `channel` INTEGER NOT NULL, `user_id` TEXT, `user_longName` TEXT, `user_shortName` TEXT, `user_hwModel` TEXT, `user_isLicensed` INTEGER, `position_latitude` REAL, `position_longitude` REAL, `position_altitude` INTEGER, `position_time` INTEGER, `position_satellitesInView` INTEGER, `position_groundSpeed` INTEGER, `position_groundTrack` INTEGER, `position_precisionBits` INTEGER, `devMetrics_time` INTEGER, `devMetrics_batteryLevel` INTEGER, `devMetrics_voltage` REAL, `devMetrics_channelUtilization` REAL, `devMetrics_airUtilTx` REAL, `envMetrics_time` INTEGER, `envMetrics_temperature` REAL, `envMetrics_relativeHumidity` REAL, `envMetrics_barometricPressure` REAL, `envMetrics_gasResistance` REAL, `envMetrics_voltage` REAL, `envMetrics_current` REAL, PRIMARY KEY(`num`))", + "fields": [ + { + "fieldPath": "num", + "columnName": "num", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "snr", + "columnName": "snr", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "rssi", + "columnName": "rssi", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastHeard", + "columnName": "lastHeard", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hopsAway", + "columnName": "hopsAway", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "channel", + "columnName": "channel", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "user.id", + "columnName": "user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "user.longName", + "columnName": "user_longName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "user.shortName", + "columnName": "user_shortName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "user.hwModel", + "columnName": "user_hwModel", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "user.isLicensed", + "columnName": "user_isLicensed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.latitude", + "columnName": "position_latitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "position.longitude", + "columnName": "position_longitude", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "position.altitude", + "columnName": "position_altitude", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.time", + "columnName": "position_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.satellitesInView", + "columnName": "position_satellitesInView", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.groundSpeed", + "columnName": "position_groundSpeed", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.groundTrack", + "columnName": "position_groundTrack", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "position.precisionBits", + "columnName": "position_precisionBits", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deviceMetrics.time", + "columnName": "devMetrics_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deviceMetrics.batteryLevel", + "columnName": "devMetrics_batteryLevel", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "deviceMetrics.voltage", + "columnName": "devMetrics_voltage", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "deviceMetrics.channelUtilization", + "columnName": "devMetrics_channelUtilization", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "deviceMetrics.airUtilTx", + "columnName": "devMetrics_airUtilTx", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.time", + "columnName": "envMetrics_time", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.temperature", + "columnName": "envMetrics_temperature", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.relativeHumidity", + "columnName": "envMetrics_relativeHumidity", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.barometricPressure", + "columnName": "envMetrics_barometricPressure", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.gasResistance", + "columnName": "envMetrics_gasResistance", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.voltage", + "columnName": "envMetrics_voltage", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "environmentMetrics.current", + "columnName": "envMetrics_current", + "affinity": "REAL", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "num" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "packet", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uuid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `port_num` INTEGER NOT NULL, `contact_key` TEXT NOT NULL, `received_time` INTEGER NOT NULL, `data` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "uuid", + "columnName": "uuid", + "affinity": "INTEGER", + "notNull": true + }, + { + "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": "data", + "columnName": "data", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uuid" + ] + }, + "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, 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 + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uuid" + ] + }, + "indices": [], + "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, '101c86e2befadaec004cbc7b90f2e961')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt index 729e7b187..cc187aceb 100644 --- a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt @@ -2,6 +2,7 @@ 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 @@ -219,6 +220,8 @@ data class NodeInfo( 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 + @ColumnInfo(name="hopsAway", defaultValue = "0") + var hopsAway: Int = 0, @Embedded(prefix = "devMetrics_") var deviceMetrics: DeviceMetrics? = null, var channel: Int = 0, 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 fa652f08b..db242cb99 100644 --- a/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt +++ b/app/src/main/java/com/geeksville/mesh/database/MeshtasticDatabase.kt @@ -27,8 +27,9 @@ import com.geeksville.mesh.database.entity.QuickChatAction autoMigrations = [ AutoMigration (from = 3, to = 4), AutoMigration (from = 4, to = 5), + AutoMigration (from = 5, to = 6), ], - version = 5, + version = 6, exportSchema = true, ) @TypeConverters(Converters::class) 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 8a9bcd6b7..0c79ed0d7 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1314,6 +1314,7 @@ class MeshService : Service(), Logging { } it.channel = info.channel + it.hopsAway = info.hopsAway } } diff --git a/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt b/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt index 3f7dc9ebe..5856eff16 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/SignalInfo.kt @@ -24,11 +24,14 @@ fun signalInfo( } else { buildString { if (nodeInfo.channel > 0) append("ch:${nodeInfo.channel}") - if (nodeInfo.snr < 100F && nodeInfo.rssi < 0) { - if (isNotEmpty()) append(" ") - append("RSSI: %d SNR: %.1f".format(nodeInfo.rssi, nodeInfo.snr)) - } + if (nodeInfo.hopsAway == 0){ + if (nodeInfo.snr < 100F && nodeInfo.rssi < 0) { + if (isNotEmpty()) append(" ") + append("RSSI: %d SNR: %.1f".format(nodeInfo.rssi, nodeInfo.snr)) + } + }else{append("Hops Away: %d".format(nodeInfo.hopsAway))} } + } return if (text.isNotEmpty()) { Text( diff --git a/app/src/main/java/com/geeksville/mesh/ui/preview/PreviewParameterProviders.kt b/app/src/main/java/com/geeksville/mesh/ui/preview/PreviewParameterProviders.kt index 789b7c869..5dccf4d23 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/preview/PreviewParameterProviders.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/preview/PreviewParameterProviders.kt @@ -35,7 +35,8 @@ class NodeInfoPreviewParameterProvider: PreviewParameterProvider { shortName = "MM", id = "mickeyMouseId", hwModel = MeshProtos.HardwareModel.TBEAM - ) + ), + hopsAway = 0 ) private val minnieMouse = mickeyMouse.copy( @@ -48,7 +49,8 @@ class NodeInfoPreviewParameterProvider: PreviewParameterProvider { ), snr = 12.5F, rssi = -42, - position = null + position = null, + hopsAway = 1 ) private val donaldDuck = NodeInfo( @@ -81,7 +83,8 @@ class NodeInfoPreviewParameterProvider: PreviewParameterProvider { gasResistance = 0.0F, voltage = 3.7F, current = 0.0F - ) + ), + hopsAway = 2 ) private val unknown = donaldDuck.copy( From 523e008e19e817120707a18d365e1b867d85bb22 Mon Sep 17 00:00:00 2001 From: DayleDrinkwater Date: Thu, 11 Apr 2024 15:32:35 +0100 Subject: [PATCH 2/8] Generate our own hopsAway, comparing hopStart to hopLimit --- .../main/java/com/geeksville/mesh/service/MeshService.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 0c79ed0d7..115b7aac6 100644 --- a/app/src/main/java/com/geeksville/mesh/service/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/service/MeshService.kt @@ -1020,7 +1020,12 @@ class MeshService : Service(), Logging { it.snr = packet.rxSnr it.rssi = packet.rxRssi } - + //generate our own hopsAway, comparing hopStart to hopLimit + if (packet.hopStart != 0){ + updateNodeInfo(fromNum){ + it.hopsAway = packet.hopStart - packet.hopLimit + } + } handleReceivedData(packet) } } From b69466f669ce360577267e9adc41a970abae0421 Mon Sep 17 00:00:00 2001 From: DayleDrinkwater Date: Fri, 12 Apr 2024 16:38:50 +0100 Subject: [PATCH 3/8] Remove import of hopsAway from device nodeInfo, as this only shows 0 when hopStart isn't included on packets (with this info, we can't differentiate between a node which is Hops Away but on old firmware, or nodes which are on new firmware but direct. Both are 0) Check if hopStart is 0 but hopLimit is not 0, if true set hopsAway to -1. Show nodes with hopsAway with -1 with a (!) appended to the RSSI details, to show this probably isn't true. (eg they are using old firmware) Change the default of hopsAway to -1, until we know it is direct (0) or hops away (1+) --- .idea/inspectionProfiles/Project_Default.xml | 20 +++++++++++++++++++ .../6.json | 8 ++++---- .../main/java/com/geeksville/mesh/NodeInfo.kt | 4 ++-- .../geeksville/mesh/service/MeshService.kt | 14 +++++++++++-- .../java/com/geeksville/mesh/ui/SignalInfo.kt | 6 ++++-- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index c266be751..44ca2d9b0 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -5,10 +5,22 @@