Save messages in CSV and fix position broadcast

pull/260/head
Vadim Furman 2021-03-17 21:00:01 -07:00
rodzic b4d562d15f
commit 78a08898fe
7 zmienionych plików z 126 dodań i 27 usunięć

Wyświetl plik

@ -20,9 +20,7 @@ import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.RemoteException
import android.text.SpannableString
import android.text.method.LinkMovementMethod
import android.text.util.Linkify
import android.view.Menu
import android.view.MenuItem
import android.view.MotionEvent
@ -43,8 +41,8 @@ import com.geeksville.android.GeeksvilleApplication
import com.geeksville.android.Logging
import com.geeksville.android.ServiceClient
import com.geeksville.concurrent.handledLaunch
import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.databinding.ActivityMainBinding
import com.geeksville.mesh.model.Channel
import com.geeksville.mesh.model.ChannelSet
import com.geeksville.mesh.model.DeviceVersion
import com.geeksville.mesh.model.UIViewModel
@ -66,9 +64,11 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import java.io.FileOutputStream
import java.nio.charset.Charset
import java.text.DateFormat
import java.util.*
import kotlin.math.roundToInt
/*
@ -132,6 +132,7 @@ class MainActivity : AppCompatActivity(), Logging,
const val RC_SIGN_IN = 12 // google signin completed
const val RC_SELECT_DEVICE =
13 // seems to be hardwired in CompanionDeviceManager to add 65536
const val CREATE_CSV_FILE = 14
}
private lateinit var binding: ActivityMainBinding
@ -555,6 +556,18 @@ class MainActivity : AppCompatActivity(), Logging,
else ->
warn("BLE device select intent failed")
}
CREATE_CSV_FILE -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.let { file_uri ->
model.allPackets.observe(this, { packets ->
if (packets != null) {
saveMessagesCSV(file_uri, packets)
model.allPackets.removeObservers(this)
}
})
}
}
}
}
}
@ -659,7 +672,7 @@ class MainActivity : AppCompatActivity(), Logging,
val curVer = DeviceVersion(info.firmwareVersion ?: "0.0.0")
val minVer = DeviceVersion("1.2.0")
if(curVer < minVer)
if (curVer < minVer)
showAlert(R.string.firmware_too_old, R.string.firmware_old)
else {
// If our app is too old/new, we probably don't understand the new radioconfig messages, so we don't read them until here
@ -869,8 +882,7 @@ class MainActivity : AppCompatActivity(), Logging,
errormsg("Device error during init ${ex.message}")
model.isConnected.value =
MeshService.ConnectionState.valueOf(service.connectionState())
}
finally {
} finally {
connectionJob = null
}
@ -1029,6 +1041,15 @@ class MainActivity : AppCompatActivity(), Logging,
fragmentTransaction.commit()
return true
}
R.id.save_messages_csv -> {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/csv"
putExtra(Intent.EXTRA_TITLE, "messages.csv")
}
startActivityForResult(intent, CREATE_CSV_FILE)
return true
}
else -> super.onOptionsItemSelected(item)
}
}
@ -1042,4 +1063,38 @@ class MainActivity : AppCompatActivity(), Logging,
errormsg("Can not find the version: ${e.message}")
}
}
private fun saveMessagesCSV(file_uri: Uri, packets: List<Packet>) {
// Extract distances to this device from position messages and put (node,SNR,distance) in
// the file_uri
val myNodeNum = model.myNodeInfo.value?.myNodeNum ?: return
applicationContext.contentResolver.openFileDescriptor(file_uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { fs ->
// Write header
fs.write(("from,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
packets.forEach {
it.proto?.let { packet_proto ->
it.position?.let { position ->
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()
)
}
}
}
}
}
}
}
}

Wyświetl plik

@ -9,7 +9,7 @@ import com.geeksville.mesh.database.entity.Packet
@Dao
interface PacketDao {
@Query("Select * from packet order by rowid desc limit 0,:maxItem")
@Query("Select * from packet order by received_date desc limit 0,:maxItem")
fun getAllPacket(maxItem: Int): LiveData<List<Packet>>
@Insert

Wyświetl plik

@ -3,6 +3,10 @@ package com.geeksville.mesh.database.entity
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.geeksville.mesh.MeshProtos
import com.geeksville.mesh.Portnums
import com.google.protobuf.TextFormat
import java.io.IOException
@Entity(tableName = "packet")
@ -12,6 +16,25 @@ data class Packet(@PrimaryKey val uuid: String,
@ColumnInfo(name = "message") val raw_message: String
) {
val proto: MeshProtos.MeshPacket?
get() {
if (message_type == "packet") {
val builder = MeshProtos.MeshPacket.newBuilder()
try {
TextFormat.getParser().merge(raw_message, builder)
return builder.build()
} catch (e: IOException) {
}
}
return null
}
val position: MeshProtos.Position?
get() {
return proto?.run {
if (hasDecoded() && decoded.portnumValue == Portnums.PortNum.POSITION_APP_VALUE) {
return MeshProtos.Position.parseFrom(decoded.payload)
}
return null
}
}
}

Wyświetl plik

@ -163,15 +163,13 @@ class MeshService : Service(), Logging {
*/
@SuppressLint("MissingPermission")
@UiThread
private fun startLocationRequests() {
private fun startLocationRequests(requestInterval: Long) {
// FIXME - currently we don't support location reading without google play
if (fusedLocationClient == null && isGooglePlayAvailable(this)) {
GeeksvilleApplication.analytics.track("location_start") // Figure out how many users needed to use the phone GPS
val request = LocationRequest.create().apply {
interval =
5 * 60 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
interval = requestInterval
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
@ -938,19 +936,28 @@ class MeshService : Service(), Logging {
private fun onNodeDBChanged() {
maybeUpdateServiceStatusNotification()
// we don't ask for GPS locations from android if our device has a built in GPS
// Note: myNodeInfo can go away if we lose connections, so it might be null
if (myNodeInfo?.hasGPS != true) {
// If we have at least one other person in the mesh, send our GPS position otherwise stop listening to GPS
serviceScope.handledLaunch(Dispatchers.Main) {
setupLocationRequest()
}
}
serviceScope.handledLaunch(Dispatchers.Main) {
if (numOnlineNodes >= 2)
startLocationRequests()
else
stopLocationRequests()
}
} else
debug("Our radio has a built in GPS, so not reading GPS in phone")
private var locationRequestInterval: Long = 0;
private fun setupLocationRequest () {
val desiredInterval: Long = if (myNodeInfo?.hasGPS == true) {
0L // no requests when device has GPS
} else if (numOnlineNodes < 2) {
5 * 60 * 1000L // send infrequently, device needs these requests to set its clock
} else {
radioConfig?.preferences?.positionBroadcastSecs?.times( 1000L) ?: 5 * 60 * 1000L
}
debug("desired location request $desiredInterval, current $locationRequestInterval")
if (desiredInterval != locationRequestInterval) {
if (locationRequestInterval > 0) stopLocationRequests()
if (desiredInterval > 0) startLocationRequests(desiredInterval)
locationRequestInterval = desiredInterval
}
}

Wyświetl plik

@ -1,5 +1,6 @@
package com.geeksville.mesh.ui
import com.geeksville.mesh.MeshProtos
import kotlin.math.cos
import kotlin.math.sin
@ -124,6 +125,14 @@ fun latLongToMeter(
return 6366000 * tt
}
// Same as above, but takes Mesh Position proto.
fun positionToMeter(a: MeshProtos.Position, b: MeshProtos.Position): Double {
return latLongToMeter(
a.latitudeI * 1e-7,
a.longitudeI * 1e-7,
b.latitudeI * 1e-7,
b.longitudeI * 1e-7)
}
/**
* Convert degrees/mins/secs to a single double
*
@ -186,4 +195,4 @@ fun bearing(
*/
fun radToBearing(rad: Double): Double {
return (Math.toDegrees(rad) + 360) % 360
}
}

Wyświetl plik

@ -20,6 +20,10 @@
android:id="@+id/advanced_settings"
app:showAsAction="withText"
android:title="@string/advanced_settings" />
<item
android:id="@+id/save_messages_csv"
app:showAsAction="withText"
android:title="@string/save_messages" />
<item
android:id="@+id/about"
android:title="@string/about"

Wyświetl plik

@ -94,4 +94,5 @@
<string name="okay">Okay</string>
<string name="must_set_region">You must set a region!</string>
<string name="region">Region</string>
<string name="save_messages">Save messages as csv...</string>
</resources>