WIP on getting new style settings/channels

pull/257/head
Kevin Hester 2021-03-02 22:12:42 +08:00
rodzic a800bd1319
commit 02198864c5
3 zmienionych plików z 100 dodań i 38 usunięć

Wyświetl plik

@ -31,8 +31,8 @@ android {
applicationId "com.geeksville.mesh"
minSdkVersion 21 // The oldest emulator image I have tried is 22 (though 21 probably works)
targetSdkVersion 29
versionCode 20150 // format is Mmmss (where M is 1+the numeric major number
versionName "1.1.50"
versionCode 20200 // format is Mmmss (where M is 1+the numeric major number
versionName "1.2.00"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// per https://developer.android.com/studio/write/vector-asset-studio

Wyświetl plik

@ -16,7 +16,8 @@ data class MyNodeInfo(
val shouldUpdate: Boolean, // this device has old firmware
val currentPacketId: Long,
val messageTimeoutMsec: Int,
val minAppVersion: Int
val minAppVersion: Int,
val maxChannels: Int
) : Parcelable {
/** A human readable description of the software/hardware version */
val firmwareString: String get() = "$model $region/$firmwareVersion"
@ -31,6 +32,7 @@ data class MyNodeInfo(
parcel.readByte() != 0.toByte(),
parcel.readLong(),
parcel.readInt(),
parcel.readInt(),
parcel.readInt()
) {
}
@ -46,6 +48,7 @@ data class MyNodeInfo(
parcel.writeLong(currentPacketId)
parcel.writeInt(messageTimeoutMsec)
parcel.writeInt(minAppVersion)
parcel.writeInt(maxChannels)
}
override fun describeContents(): Int {

Wyświetl plik

@ -423,7 +423,7 @@ class MeshService : Service(), Logging {
private var radioConfig: RadioConfigProtos.RadioConfig? = null
private var channels = listOf<ChannelProtos.Channel>()
private var channels = arrayOf<ChannelProtos.Channel>()
/// True after we've done our initial node db init
@Volatile
@ -539,9 +539,9 @@ class MeshService : Service(), Logging {
}.build()
}
// FIXME, send channels to device!
TODO("Need to send channels to device")
channels = asChannels
channels = asChannels.toTypedArray()
}
/// Generate a new mesh packet builder with our node as the sender, and the specified node num
@ -567,7 +567,7 @@ class MeshService : Service(), Logging {
*/
private fun MeshProtos.MeshPacket.Builder.buildMeshPacket(
wantAck: Boolean = false,
id: Int = 0,
id: Int = generatePacketId(), // always assign a packet ID if we didn't already have one
hopLimit: Int = 0,
priority: MeshPacket.Priority = MeshPacket.Priority.UNSET,
initFn: MeshProtos.Data.Builder.() -> Unit
@ -702,6 +702,9 @@ class MeshService : Service(), Logging {
// if (p.hasUser()) handleReceivedUser(fromNum, p.user)
/// We tell other apps about most message types, but some may have sensitve data, so that is not shared'
var shouldBroadcast = true
when (data.portnumValue) {
Portnums.PortNum.TEXT_MESSAGE_APP_VALUE -> {
debug("Received CLEAR_TEXT from $fromId")
@ -728,10 +731,20 @@ class MeshService : Service(), Logging {
else
handleAckNak(false, data.requestId)
}
Portnums.PortNum.ADMIN_APP_VALUE -> {
val u = AdminProtos.AdminMessage.parseFrom(data.payload)
handleReceivedAdmin(packet.from, u)
shouldBroadcast = false
}
else ->
debug("No custom processing needed for ${data.portnumValue}")
}
// We always tell other apps when new data packets arrive
serviceBroadcasts.broadcastReceivedData(dataPacket)
if (shouldBroadcast)
serviceBroadcasts.broadcastReceivedData(dataPacket)
GeeksvilleApplication.analytics.track(
"num_data_receive",
@ -748,6 +761,35 @@ class MeshService : Service(), Logging {
}
}
private fun handleReceivedAdmin(fromNodeNum: Int, a: AdminProtos.AdminMessage) {
// For the time being we only care about admin messages from our local node
if (fromNodeNum == myNodeNum) {
when (a.variantCase) {
AdminProtos.AdminMessage.VariantCase.GET_RADIO_RESPONSE -> {
radioConfig = a.getRadioResponse
requestChannel(0) // Now start reading channels
}
AdminProtos.AdminMessage.VariantCase.GET_CHANNEL_RESPONSE -> {
val mi = myNodeInfo
if (mi != null) {
val ch = a.getChannelResponse
channels[ch.index] = ch
if (ch.index + 1 < mi.maxChannels) {
// Not done yet, request next channel
requestChannel(ch.index + 1)
} else {
onHasSettings()
}
}
}
else ->
warn("No special processing needed for ${a.variantCase}")
}
}
}
/// Update our DB of users based on someone sending out a User subpacket
private fun handleReceivedUser(fromNum: Int, p: MeshProtos.User) {
updateNodeInfo(fromNum) {
@ -1192,12 +1234,23 @@ class MeshService : Service(), Logging {
),
currentPacketId.toLong() and 0xffffffffL,
if (messageTimeoutMsec == 0) 5 * 60 * 1000 else messageTimeoutMsec, // constants from current device code
minAppVersion
minAppVersion,
maxChannels
)
}
newMyNodeInfo = mi
// We'll need to get a new set of channels and settings now
radioConfig = null
// prefill the channel array with null channels
channels = Array(mi.maxChannels) {
val b = ChannelProtos.Channel.newBuilder()
b.index = it
b.build()
}
/// Track types of devices and firmware versions in use
GeeksvilleApplication.analytics.setUserInfo(
DataPair("region", mi.region),
@ -1272,6 +1325,18 @@ class MeshService : Service(), Logging {
}
}
/// If we've received our initial config, our radio settings and all of our channels, send any queueed packets and broadcast connected to clients
private fun onHasSettings() {
processEarlyPackets() // send receive any packets that were queued up
// broadcast an intent with our new connection state
serviceBroadcasts.broadcastConnection()
onNodeDBChanged()
reportConnection()
updateRegion()
}
private fun handleConfigComplete(configCompleteId: Int) {
if (configCompleteId == configNonce) {
@ -1295,19 +1360,25 @@ class MeshService : Service(), Logging {
newNodes.clear() // Just to save RAM ;-)
haveNodeDB = true // we now have nodes from real hardware
processEarlyPackets() // send receive any packets that were queued up
// broadcast an intent with our new connection state
serviceBroadcasts.broadcastConnection()
onNodeDBChanged()
reportConnection()
updateRegion()
requestRadioConfig()
requestChannel(0)
}
} else
warn("Ignoring stale config complete")
}
private fun requestRadioConfig() {
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket {
getRadioRequest = true
})
}
private fun requestChannel(channelIndex: Int) {
sendToRadio(newMeshPacketTo(myNodeNum).buildAdminPacket {
getChannelRequest = channelIndex + 1
})
}
/**
* Start the modern (REV2) API configuration flow
*/
@ -1430,36 +1501,24 @@ class MeshService : Service(), Logging {
throw Exception("Can't set user without a node info") // this shouldn't happen
}
/// Do not use directly, instead call generatePacketId()
private var currentPacketId = 0L
private var currentPacketId = Random(System.currentTimeMillis()).nextLong().absoluteValue
/**
* Generate a unique packet ID (if we know enough to do so - otherwise return 0 so the device will do it)
*/
@Synchronized
private fun generatePacketId(): Int {
val numPacketIds =
((1L shl 32) - 1).toLong() // A mask for only the valid packet ID bits, either 255 or maxint
myNodeInfo?.let {
val numPacketIds =
((1L shl 32) - 1).toLong() // A mask for only the valid packet ID bits, either 255 or maxint
currentPacketId++
if (currentPacketId == 0L) {
// We now always pick a random initial packet id (odds of collision with the device is insanely low with 32 bit ids)
val random = Random(System.currentTimeMillis())
val devicePacketId = random.nextLong().absoluteValue
currentPacketId = currentPacketId and 0xffffffff // keep from exceeding 32 bits
// Not inited - pick a number on the opposite side of what the device is using
currentPacketId = devicePacketId + numPacketIds / 2
} else {
currentPacketId++
}
currentPacketId = currentPacketId and 0xffffffff // keep from exceeding 32 bits
// Use modulus and +1 to ensure we skip 0 on any values we return
return ((currentPacketId % numPacketIds) + 1L).toInt()
}
return 0 // We don't have mynodeinfo yet, so just let the radio eventually assign an ID
// Use modulus and +1 to ensure we skip 0 on any values we return
return ((currentPacketId % numPacketIds) + 1L).toInt()
}
var firmwareUpdateFilename: UpdateFilenames? = null