From 75d35b87beb19e87237af063ac3076f7bb8f1da4 Mon Sep 17 00:00:00 2001 From: geeksville Date: Fri, 24 Jan 2020 20:35:42 -0800 Subject: [PATCH] IPC implementation is more real --- TODO.md | 2 +- app/build.gradle | 10 ++- app/src/main/AndroidManifest.xml | 1 + .../java/com/geeksville/mesh/MeshService.kt | 82 +++++++++++++++++-- .../geeksville/mesh/RadioInterfaceService.kt | 15 +++- 5 files changed, 97 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index c6cd45c6d..6d463fca6 100644 --- a/TODO.md +++ b/TODO.md @@ -29,7 +29,7 @@ nanopb binaries available here: https://jpa.kapsi.fi/nanopb/download/ use nanopb * require user auth to pair with the device (i.e. press button on device to allow a new phone to pair with it). Don't leave device discoverable. Don't let unpaired users do thing with device * remove example code boilerplate from the service - +* switch from protobuf-java to protobuf-javalite - much faster and smaller, just no JSON debug printing # Low priority diff --git a/app/build.gradle b/app/build.gradle index 92fa3ff64..3b1125e73 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,7 +53,8 @@ protobuf { all().each { task -> task.builtins { java { - option "lite" + // turned off for now so I can use json printing + // option "lite" } } } @@ -62,7 +63,7 @@ protobuf { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' @@ -72,7 +73,10 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' // You need to depend on the lite runtime library, not protobuf-java - implementation 'com.google.protobuf:protobuf-javalite:3.8.0' + // For now I'm not using javalite, because I want JSON printing + implementation 'com.google.protobuf:protobuf-java:3.11.1' + implementation 'com.google.protobuf:protobuf-java-util:3.11.1' + // implementation 'com.google.protobuf:protobuf-javalite:3.11.1' // You also need to include the following Compose toolkit dependencies. implementation("androidx.compose:compose-runtime:$compose_version") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e14921688..978e0c377 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,6 +60,7 @@ diff --git a/app/src/main/java/com/geeksville/mesh/MeshService.kt b/app/src/main/java/com/geeksville/mesh/MeshService.kt index 9f540f9c2..476dcbe1b 100644 --- a/app/src/main/java/com/geeksville/mesh/MeshService.kt +++ b/app/src/main/java/com/geeksville/mesh/MeshService.kt @@ -7,7 +7,9 @@ import android.content.Intent import android.content.IntentFilter import android.os.IBinder import com.geeksville.android.Logging - +import com.geeksville.mesh.MeshProtos.MeshPacket +import com.geeksville.mesh.MeshProtos.ToRadio +import com.google.protobuf.ByteString /** * Handles all the communication with android apps. Also keeps an internal model @@ -43,6 +45,11 @@ class MeshService : Service(), Logging { sendBroadcast(intent) } + /// Send a command/packet to our radio + private fun sendToRadio(p: ToRadio.Builder) { + RadioInterfaceService.sendToRadio(this, p.build().toByteArray()) + } + override fun onBind(intent: Intent): IBinder { // Return the interface return binder @@ -53,6 +60,13 @@ class MeshService : Service(), Logging { val filter = IntentFilter(RadioInterfaceService.RECEIVE_FROMRADIO_ACTION) registerReceiver(radioInterfaceReceiver, filter) + + // FIXME - don't do this until after we see that the radio is connected to the phone + // Ask for the current node DB + sendToRadio(ToRadio.newBuilder().apply { + wantNodes = ToRadio.WantNodes.newBuilder().build() + }) + } override fun onDestroy() { @@ -60,6 +74,43 @@ class MeshService : Service(), Logging { super.onDestroy() } + /// Is our radio connected to the phone? + private var isConnected = false + + /// We learn this from the node db sent by the device - it is stable for the entire session + private var ourNodeNum = -1 + + // model objects that directly map to the corresponding protobufs + data class MeshUser(val id: String, val longName: String, val shortName: String) + + data class Position(val latitude: Double, val longitude: Double, val altitude: Int) + data class NodeInfo( + val num: Int, + val user: MeshUser, + val position: Position, + val lastSeen: Long + ) + + // The database of active nodes, index is the node number + private val nodeDBbyNodeNum = mutableMapOf() + + /// The database of active nodes, index is the node user ID string + private val nodeDBbyID = mutableMapOf() + + /// Map a userid to a node num, or throw an exception if not found + private fun idToNodeNum(id: String) = nodeDBbyID.getValue(id).num + + /// Generate a new mesh packet builder with our node as the sender, and the specified node num + private + + fun newMeshPacketTo(idNum: Int) = MeshPacket.newBuilder().apply { + from = ourNodeNum + to = idNum + } + + /// Generate a new mesh packet builder with our node as the sender, and the specified recipient + private fun newMeshPacketTo(id: String) = newMeshPacketTo(idToNodeNum(id)) + /** * Receives messages from our BT radio service and processes them to update our model * and send to clients as needed. @@ -72,23 +123,40 @@ class MeshService : Service(), Logging { } } + private val binder = object : IMeshService.Stub() { override fun setOwner(myId: String, longName: String, shortName: String) { error("TODO setOwner $myId : $longName : $shortName") } - override fun sendOpaque(destId: String, payload: ByteArray) { - error("TODO sendOpaque $destId <- ${payload.size}") + override fun sendOpaque(destId: String, payloadIn: ByteArray) { + info("sendOpaque $destId <- ${payloadIn.size}") + + // encapsulate our payload in the proper protobufs and fire it off + val packet = newMeshPacketTo(destId).apply { + payload = MeshProtos.MeshPayload.newBuilder().apply { + addSubPackets(MeshProtos.SubPacket.newBuilder().apply { + opaque = MeshProtos.Opaque.newBuilder().apply { + payload = ByteString.copyFrom(payloadIn) + }.build() + }.build()) + }.build() + }.build() + + sendToRadio(ToRadio.newBuilder().apply { + this.packet = packet + }) } override fun getOnline(): Array { - error("TODO getOnline") - return arrayOf("+16508675309") + info("getOnline") + // return arrayOf("+16508675309") + return nodeDBbyID.keys.toTypedArray() } override fun isConnected(): Boolean { - error("TODO isConnected") - return true + info("isConnected") + return isConnected } } } \ No newline at end of file diff --git a/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt index 8124090de..e4dc8d8db 100644 --- a/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt +++ b/app/src/main/java/com/geeksville/mesh/RadioInterfaceService.kt @@ -5,6 +5,7 @@ import android.content.Intent import androidx.core.app.JobIntentService import com.geeksville.android.DebugLogFile import com.geeksville.android.Logging +import com.google.protobuf.util.JsonFormat const val EXTRA_CONNECTED = "$prefix.Connected" const val EXTRA_PAYLOAD = "$prefix.Payload" @@ -58,9 +59,12 @@ class RadioInterfaceService : JobIntentService(), Logging { i.putExtra(EXTRA_PAYLOAD, a) enqueueWork(context, i) } + + // for debug logging only + private val jsonPrinter = JsonFormat.printer() } - val sentPacketsLog = DebugLogFile(this, "sent_log.json") + lateinit var sentPacketsLog: DebugLogFile // inited in onCreate private fun broadcastReceivedFromRadio(payload: ByteArray) { val intent = Intent(RECEIVE_FROMRADIO_ACTION) @@ -80,7 +84,8 @@ class RadioInterfaceService : JobIntentService(), Logging { // For debugging/logging purposes ONLY we convert back into a protobuf for readability val proto = MeshProtos.ToRadio.parseFrom(p) info("TODO sending to radio: $proto") - sentPacketsLog.log("FIXME JSON") + val json = jsonPrinter.print(proto).replace('\n', ' ') + sentPacketsLog.log(json) } // Handle an incoming packet from the radio, broadcasts it as an android intent @@ -88,6 +93,12 @@ class RadioInterfaceService : JobIntentService(), Logging { broadcastReceivedFromRadio(p) } + override fun onCreate() { + super.onCreate() + + sentPacketsLog = DebugLogFile(this, "sent_log.json") + } + override fun onDestroy() { sentPacketsLog.close() super.onDestroy()