clean up protobufs with a bit of futureproofing for new packet types

This commit is contained in:
geeksville 2020-01-25 06:16:10 -08:00
parent 94e0255325
commit 7fb0cabee8
4 changed files with 77 additions and 34 deletions

View file

@ -1,11 +1,11 @@
* handle failures in onCharWrite, instead of logAssert - because they can happen if device goes away
* use android service from Signal
* DONE handle failures in onCharWrite, instead of logAssert - because they can happen if device goes away
* make test implementation of android service (doesn't use bluetooth)
* clean up sw update code in device side
* DONE add broadcasters for use by signal (node changes and packet received)
* make compose based access show mesh state
* use android service from Signal
* add real messaging code/protobufs
* use https://codelabs.developers.google.com/codelabs/jetpack-compose-basics/#4 to show service state
* connect to bluetooth device automatically using minimum power
@ -35,7 +35,7 @@ Don't leave device discoverable. Don't let unpaired users do thing with device
* make analytics optional
* possibly use finotes for analytics https://finotes.com/
* also add a receiver that fires after a new update was installed from the play stoe
* also add a receiver that fires after a new update was installed from the play stoe
# Done

View file

@ -11,8 +11,10 @@ interface IMeshService {
/*
Send an opaque packet to a specified node name
typ is defined in mesh.proto Data.Type. For now juse use 0 to mean opaque bytes.
*/
void sendOpaque(String destId, in byte[] payload);
void sendOpaque(String destId, in byte[] payload, int typ);
/**
Get the IDs of everyone on the mesh. You should also subscribe for NODE_CHANGE broadcasts.

View file

@ -10,6 +10,7 @@ import com.geeksville.android.Logging
import com.geeksville.mesh.MeshProtos.MeshPacket
import com.geeksville.mesh.MeshProtos.ToRadio
import com.google.protobuf.ByteString
import java.nio.charset.Charset
/**
* Handles all the communication with android apps. Also keeps an internal model
@ -115,9 +116,8 @@ class MeshService : Service(), Logging {
private fun toNodeID(n: Int) = toNodeInfo(n).user?.id
/// given a nodenum, return a db entry - creating if necessary
private
fun getOrCreateNodeInfo(n: Int) = nodeDBbyNodeNum.getOrPut(n) { -> NodeInfo(n) }
private fun getOrCreateNodeInfo(n: Int) =
nodeDBbyNodeNum.getOrPut(n) { -> NodeInfo(n) }
/// Map a userid to a node/ node num, or throw an exception if not found
private fun toNodeInfo(id: String) = nodeDBbyID.getValue(id)
@ -139,6 +139,38 @@ class MeshService : Service(), Logging {
/// Generate a new mesh packet builder with our node as the sender, and the specified recipient
private fun newMeshPacketTo(id: String) = newMeshPacketTo(toNodeNum(id))
/// Update our model and resend as needed for a MeshPacket we just received from the radio
private fun handleReceivedData(fromNum: Int, data: MeshProtos.Data) {
val bytes = data.payload.toByteArray()
val fromId = toNodeID(fromNum)
/// the sending node ID if possible, else just its number
val fromString = fromId ?: fromId.toString()
when (data.typValue) {
MeshProtos.Data.Type.SIMPLE_TEXT_VALUE ->
warn(
"TODO ignoring SIMPLE_TEXT from $fromString: ${bytes.toString(
Charset.forName("UTF-8")
)}"
)
MeshProtos.Data.Type.CLEAR_READACK_VALUE ->
warn(
"TODO ignoring CLEAR_READACK from $fromString"
)
MeshProtos.Data.Type.SIGNAL_OPAQUE_VALUE ->
if (fromId == null)
error("Ignoring opaque from $fromNum because we don't yet know its ID")
else {
debug("Received opaque from $fromId ${bytes.size}")
broadcastReceivedOpaque(fromId, bytes)
}
else -> TODO()
}
}
/// Update our model and resend as needed for a MeshPacket we just received from the radio
private fun handleReceivedMeshPacket(packet: MeshPacket) {
val fromNum = packet.from
@ -162,19 +194,9 @@ class MeshService : Service(), Logging {
updateNodeInfo(fromNum) {
it.lastSeen = p.time.msecs
}
MeshProtos.SubPacket.TEXT_FIELD_NUMBER -> {
debug("TODO Ignoring TEXT from $fromNum ${p.text.text}")
}
MeshProtos.SubPacket.OPAQUE_FIELD_NUMBER -> {
val opaque = p.opaque.payload.toByteArray()
val fromId = toNodeID(fromNum)
if (fromId == null)
error("Ignoring opaque from $fromNum because we don't yet know its ID")
else {
debug("Received opaque from $fromId ${opaque.size}")
broadcastReceivedOpaque(fromId, opaque)
}
}
MeshProtos.SubPacket.DATA_FIELD_NUMBER ->
handleReceivedData(fromNum, p.data)
MeshProtos.SubPacket.USER_FIELD_NUMBER ->
updateNodeInfo(fromNum) {
it.user = MeshUser(p.user.id, p.user.longName, p.user.shortName)
@ -195,6 +217,10 @@ class MeshService : Service(), Logging {
}
}
private fun handleReceivedNodeInfo(info: MeshProtos.NodeInfo) {
TODO()
}
/**
* Receives messages from our BT radio service and processes them to update our model
* and send to clients as needed.
@ -206,7 +232,7 @@ class MeshService : Service(), Logging {
info("Received from radio service: $proto")
when (proto.variantCase.number) {
MeshProtos.FromRadio.PACKET_FIELD_NUMBER -> handleReceivedMeshPacket(proto.packet)
MeshProtos.FromRadio.NODE_INFO_FIELD_NUMBER -> TODO()
MeshProtos.FromRadio.NODE_INFO_FIELD_NUMBER -> handleReceivedNodeInfo(proto.nodeInfo)
else -> TODO("Unexpected FromRadio variant")
}
}
@ -217,15 +243,16 @@ class MeshService : Service(), Logging {
error("TODO setOwner $myId : $longName : $shortName")
}
override fun sendOpaque(destId: String, payloadIn: ByteArray) {
override fun sendOpaque(destId: String, payloadIn: ByteArray, typ: Int) {
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)
data = MeshProtos.Data.newBuilder().also {
it.typ = MeshProtos.Data.Type.SIGNAL_OPAQUE
it.payload = ByteString.copyFrom(payloadIn)
}.build()
}.build())
}.build()

View file

@ -53,13 +53,28 @@ message Time {
uint64 msecs = 1; // msecs since 1970
}
// A message sent from a device outside of the mesh, in a form the mesh does not understand
// i.e. a Signal app level message.
message Opaque {
bytes payload = 1;
// a data message to forward to an external app
message Data {
enum Type {
/// A message sent from a device outside of the mesh, in a form the mesh does not understand
SIGNAL_OPAQUE = 0; // NOTE: This must be 0, because it is documented in IMeshService.aidl to be so
/// a simple UTF-8 text message, which even the little micros in the mesh can understand and show on their screen
/// eventually in some circumstances even signal might send messages in this form (see below)
SIMPLE_TEXT = 1;
/// a message receive acknowledgement, sent in cleartext - allows radio to show user that a message has been read by the recpient, optional
CLEAR_READACK = 2;
/// Not yet used but eventually:
/// SIGNAL_CLEAR_OPAQUE = 3; // Unencrypted at the signal level, relying on the radio crypt only (to keep size small), but contains Signal control data radios don't care about (or non ascii text)
}
Type typ = 1; // required
bytes payload = 2; // required
}
// a simple text message, which even the little micros in the mesh can understand and show on their screen
message Text {
string text = 1;
}
@ -87,11 +102,10 @@ message SubPacket {
oneof variant {
Position position = 1;
Time time = 2;
Text text = 3;
Opaque opaque = 4;
User user = 5;
WantNodeNum want_node = 6;
DenyNodeNum deny_node = 7;
Data data = 3;
User user = 4;
WantNodeNum want_node = 5;
DenyNodeNum deny_node = 6;
}
}