Merge remote-tracking branch 'root/master' into dev1.2

# Conflicts:
#	app/src/main/java/com/geeksville/mesh/MainActivity.kt
This commit is contained in:
Kevin Hester 2021-03-24 13:49:46 +08:00
commit 5f6cb7637c
6 changed files with 50 additions and 18 deletions

View file

@ -61,11 +61,9 @@ import com.google.android.material.tabs.TabLayoutMediator
import com.google.protobuf.InvalidProtocolBufferException
import com.vorlonsoft.android.rate.AppRate
import com.vorlonsoft.android.rate.StoreType
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.*
import java.io.FileOutputStream
import java.lang.Runnable
import java.nio.charset.Charset
import java.text.DateFormat
import java.util.*
@ -556,10 +554,16 @@ class MainActivity : AppCompatActivity(), Logging,
CREATE_CSV_FILE -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.let { file_uri ->
// model.allPackets is a result of a query, so we need to use observer for
// the query to materialize
model.allPackets.observe(this, { packets ->
if (packets != null) {
saveMessagesCSV(file_uri, packets)
// no need for observer once got non-null list
model.allPackets.removeObservers(this)
// execute on the default thread pool to not block the main thread
CoroutineScope(Dispatchers.Default + Job()).handledLaunch {
saveMessagesCSV(file_uri, packets)
}
}
})
}
@ -1087,7 +1091,7 @@ class MainActivity : AppCompatActivity(), Logging,
applicationContext.contentResolver.openFileDescriptor(file_uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { fs ->
// Write header
fs.write(("from,snr,time,dist\n").toByteArray());
fs.write(("from,rssi,snr,time,dist\n").toByteArray());
// Packets are ordered by time, we keep most recent position of
// our device in my_position.
var my_position: MeshProtos.Position? = null
@ -1097,13 +1101,9 @@ class MainActivity : AppCompatActivity(), Logging,
if (packet_proto.from == myNodeNum) {
my_position = position
} else if (my_position != null) {
val dist: Int =
positionToMeter(my_position!!, position).roundToInt()
fs.write(
("${packet_proto.from.toUInt().toString(16)}," +
"${packet_proto.rxSnr},${packet_proto.rxTime},$dist\n")
.toByteArray()
)
val dist = positionToMeter(my_position!!, position).roundToInt()
fs.write("%x,%d,%f,%d,%d\n".format(packet_proto.from,packet_proto.rxRssi,
packet_proto.rxSnr, packet_proto.rxTime, dist).toByteArray())
}
}
}

View file

@ -82,7 +82,9 @@ data class Position(
data class NodeInfo(
val num: Int, // This is immutable, and used as a key
var user: MeshUser? = null,
var position: Position? = null
var position: Position? = null,
var snr: Float = Float.MAX_VALUE,
var rssi: Int = Int.MAX_VALUE
) : Parcelable {
/// Return the last time we've seen this node in secs since 1970

View file

@ -717,6 +717,7 @@ class MeshService : Service(), Logging {
// Handle new style position info
Portnums.PortNum.POSITION_APP_VALUE -> {
val u = MeshProtos.Position.parseFrom(data.payload)
debug("position_app ${packet.from} ${u.toOneLineString()}")
handleReceivedPosition(packet.from, u, dataPacket.time)
}
@ -827,6 +828,7 @@ class MeshService : Service(), Logging {
defaultTime: Long = System.currentTimeMillis()
) {
updateNodeInfo(fromNum) {
debug("update ${it.user?.longName} with ${p.toOneLineString()}")
it.position = Position(p)
updateNodeInfoTime(it, (defaultTime / 1000).toInt())
}
@ -923,6 +925,8 @@ class MeshService : Service(), Logging {
updateNodeInfo(fromNum) {
// Update our last seen based on any valid timestamps. If the device didn't provide a timestamp make one
updateNodeInfoTime(it, rxTime)
it.snr = packet.rxSnr
it.rssi = packet.rxRssi
}
handleReceivedData(packet)

View file

@ -21,6 +21,7 @@ import com.geeksville.mesh.databinding.NodelistFragmentBinding
import com.geeksville.mesh.model.UIViewModel
import com.geeksville.util.formatAgo
import java.net.URLEncoder
import kotlin.math.roundToInt
class UsersFragment : ScreenFragment("Users"), Logging {
@ -41,6 +42,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
val batteryPctView = itemView.batteryPercentageView
val lastTime = itemView.lastConnectionView
val powerIcon = itemView.batteryIcon
val signalView = itemView.signalView
}
private val nodesAdapter = object : RecyclerView.Adapter<ViewHolder>() {
@ -137,10 +139,22 @@ class UsersFragment : ScreenFragment("Users"), Logging {
} else {
holder.distanceView.visibility = View.INVISIBLE
}
renderBattery(n.batteryPctLevel, holder)
holder.lastTime.text = formatAgo(n.lastSeen);
if ((n.num == ourNodeInfo?.num) || (n.snr > 100f)) {
holder.signalView.visibility = View.INVISIBLE
} else {
val text = if (n.rssi < 0) {
"rssi:${n.rssi} snr:${n.snr.roundToInt()}"
} else {
// Older devices do not send rssi. Remove this branch once upgraded past 1.2.1
"snr:${n.snr.roundToInt()}"
}
holder.signalView.text = text
holder.signalView.visibility = View.VISIBLE
}
}
private var nodes = arrayOf<NodeInfo>()