progress on keeeping gps off

1.2-legacy
geeksville 2020-02-19 10:53:36 -08:00
rodzic aab062b06f
commit 5b83320a69
5 zmienionych plików z 119 dodań i 72 usunięć

Wyświetl plik

@ -7,6 +7,7 @@ the channel is encrypted, you can share the the channel key with others by qr co
* take video
* make a working currently vs not working list
* show offline nodes as greyed out
* make node list view not look like ass
* record analytics events when radio connects/disconnects, include # of nodes in mesh
* make channel button look like a button

Wyświetl plik

@ -37,11 +37,17 @@ 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 Position(
val latitude: Double,
val longitude: Double,
val altitude: Int,
val time: Int = (System.currentTimeMillis() / 1000).toInt() // default to current time in secs
) :
Parcelable {
constructor(parcel: Parcel) : this(
parcel.readDouble(),
parcel.readDouble(),
parcel.readInt(),
parcel.readInt()
) {
}
@ -56,6 +62,7 @@ data class Position(val latitude: Double, val longitude: Double, val altitude: I
parcel.writeDouble(latitude)
parcel.writeDouble(longitude)
parcel.writeInt(altitude)
parcel.writeInt(time)
}
override fun describeContents(): Int {
@ -77,17 +84,32 @@ data class Position(val latitude: Double, val longitude: Double, val altitude: I
data class NodeInfo(
val num: Int, // This is immutable, and used as a key
var user: MeshUser? = null,
var position: Position? = null,
var lastSeen: Int? = null
var position: Position? = null
) : Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readParcelable(MeshUser::class.java.classLoader),
parcel.readParcelable(Position::class.java.classLoader),
parcel.readValue(Int::class.java.classLoader) as? Int
parcel.readParcelable(Position::class.java.classLoader)
) {
}
/// Return the last time we've seen this node in secs since 1970
val lastSeen get() = position?.time ?: 0
/**
* true if the device was heard from recently
*
* Note: if a node has never had its time set, it will have a time of zero. In that
* case assume it is online - so that we will start sending GPS updates
*/
val isOnline: Boolean
get() {
val now = System.currentTimeMillis() / 1000
// FIXME - use correct timeout from the device settings
val timeout = 5 * 60
return (now - lastSeen <= timeout) || lastSeen == 0
}
/// @return distance in meters to some other node (or null if unknown)
fun distance(o: NodeInfo?): Int? {
val p = position
@ -111,7 +133,6 @@ data class NodeInfo(
parcel.writeInt(num)
parcel.writeParcelable(user, flags)
parcel.writeParcelable(position, flags)
parcel.writeValue(lastSeen)
}
override fun describeContents(): Int {

Wyświetl plik

@ -23,8 +23,7 @@ object NodeDB {
"Kevin MesterNoLoc",
"KLO"
),
null,
12345
null
)
val testNodes = testPositions.mapIndexed { index, it ->
@ -35,8 +34,7 @@ object NodeDB {
"Kevin Mester$index",
"KM$index"
),
it,
12345
it
)
}

Wyświetl plik

@ -132,57 +132,62 @@ class MeshService : Service(), Logging {
private var fusedLocationClient: FusedLocationProviderClient? = null
/**
* start our location requests
* start our location requests (if they weren't already running)
*
* per https://developer.android.com/training/location/change-location-settings
*/
@SuppressLint("MissingPermission")
private fun startLocationRequests() {
val request = LocationRequest.create().apply {
interval =
60 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
if (fusedLocationClient == null) {
val request = LocationRequest.create().apply {
interval =
20 * 1000 // FIXME, do more like once every 5 mins while we are connected to our radio _and_ someone else is in the mesh
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
val locationClient = LocationServices.getSettingsClient(this)
val locationSettingsResponse = locationClient.checkLocationSettings(builder.build())
locationSettingsResponse.addOnSuccessListener {
debug("We are now successfully listening to the GPS")
}
locationSettingsResponse.addOnFailureListener { exception ->
error("Failed to listen to GPS")
if (exception is ResolvableApiException) {
exceptionReporter {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
// FIXME
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
/* exception.startResolutionForResult(
this@MainActivity,
REQUEST_CHECK_SETTINGS
) */
}
} else
reportException(exception)
}
val client = LocationServices.getFusedLocationProviderClient(this)
// FIXME - should we use Looper.myLooper() in the third param per https://github.com/android/location-samples/blob/432d3b72b8c058f220416958b444274ddd186abd/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
client.requestLocationUpdates(request, locationCallback, null)
fusedLocationClient = client
}
val builder = LocationSettingsRequest.Builder().addLocationRequest(request)
val locationClient = LocationServices.getSettingsClient(this)
val locationSettingsResponse = locationClient.checkLocationSettings(builder.build())
locationSettingsResponse.addOnSuccessListener {
debug("We are now successfully listening to the GPS")
}
locationSettingsResponse.addOnFailureListener { exception ->
error("Failed to listen to GPS")
if (exception is ResolvableApiException) {
exceptionReporter {
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
// FIXME
// Show the dialog by calling startResolutionForResult(),
// and check the result in onActivityResult().
/* exception.startResolutionForResult(
this@MainActivity,
REQUEST_CHECK_SETTINGS
) */
}
} else
reportException(exception)
}
val client = LocationServices.getFusedLocationProviderClient(this)
// FIXME - should we use Looper.myLooper() in the third param per https://github.com/android/location-samples/blob/432d3b72b8c058f220416958b444274ddd186abd/LocationUpdatesForegroundService/app/src/main/java/com/google/android/gms/location/sample/locationupdatesforegroundservice/LocationUpdatesService.java
client.requestLocationUpdates(request, locationCallback, null)
fusedLocationClient = client
}
private fun stopLocationRequests() {
fusedLocationClient?.removeLocationUpdates(locationCallback)
fusedLocationClient = null
if (fusedLocationClient != null) {
debug("Stopping location requests")
fusedLocationClient?.removeLocationUpdates(locationCallback)
fusedLocationClient = null
}
}
/**
@ -386,7 +391,11 @@ class MeshService : Service(), Logging {
id
)
// ?: getOrCreateNodeInfo(10) // FIXME hack for now - throw IdNotFoundException(id)
/**
* How many nodes are currently online (including our local node)
*/
private val numOnlineNodes get() = nodeDBbyNodeNum.values.count { it.isOnline }
private fun toNodeNum(id: String) = toNodeInfo(id).num
@ -497,7 +506,9 @@ class MeshService : Service(), Logging {
it.position = Position(
p.latitude,
p.longitude,
p.altitude
p.altitude,
if (p.time != 0) p.time else it.position?.time
?: 0 // if this position didn't include time, just keep our old one
)
}
}
@ -512,11 +523,11 @@ class MeshService : Service(), Logging {
val p = packet.payload
// Update our last seen based on any valid timestamps
if (packet.rxTime != 0) {
updateNodeInfo(fromNum) {
it.lastSeen = packet.rxTime
}
// Update our last seen based on any valid timestamps. If the device didn't provide a timestamp make one
val lastSeen =
if (packet.rxTime != 0) packet.rxTime else (System.currentTimeMillis() / 1000).toInt()
updateNodeInfo(fromNum) {
it.position = it.position?.copy(time = lastSeen)
}
when (p.variantCase.number) {
@ -530,6 +541,8 @@ class MeshService : Service(), Logging {
handleReceivedUser(fromNum, p.user)
else -> TODO("Unexpected SubPacket variant")
}
onNodeDBChanged()
}
/// We are reconnecting to a radio, redownload the full state. This operation might take hundreds of milliseconds
@ -566,17 +579,32 @@ class MeshService : Service(), Logging {
it.position = Position(
info.position.latitude,
info.position.longitude,
info.position.altitude
info.position.altitude,
info.position.time
)
it.lastSeen = info.lastSeen
}
// advance to next
infoBytes = connectedRadio.readNodeInfo()
}
onNodeDBChanged()
}
/// If we just changed our nodedb, we might want to do somethings
private fun onNodeDBChanged() {
// we don't ask for GPS locations from android if our device has a built in GPS
if (!myNodeInfo!!.hasGPS) {
// If we have at least one other person in the mesh, send our GPS position otherwise stop listening to GPS
if (numOnlineNodes >= 2)
startLocationRequests()
else
stopLocationRequests()
} else
debug("Our radio has a built in GPS, so not reading GPS in phone")
}
/// Called when we gain/lose connection to our radio
private fun onConnectionChanged(c: Boolean) {
@ -586,12 +614,6 @@ class MeshService : Service(), Logging {
// Do our startup init
try {
reinitFromRadio()
// we don't ask for GPS locations from android if our device has a built in GPS
if (!myNodeInfo!!.hasGPS)
startLocationRequests()
else
debug("Our radio has a built in GPS, so not reading GPS in phone")
} catch (ex: RemoteException) {
// It seems that when the ESP32 goes offline it can briefly come back for a 100ms ish which
// causes the phone to try and reconnect. If we fail downloading our initial radio state we don't want to
@ -662,6 +684,7 @@ class MeshService : Service(), Logging {
it.latitude = lat
it.longitude = lon
it.altitude = alt
it.time = (System.currentTimeMillis() / 1000).toInt() // Include our current timestamp
}.build()
// encapsulate our payload in the proper protobufs and fire it off

Wyświetl plik

@ -62,6 +62,9 @@ message Position {
/// true if this position came from the GPS inside the esp32 board, false if it was from a helper app on the phone
bool from_hardware = 5;
/// This is usually not sent over the mesh (to save space), but it is sent from the phone so that the local device can set its RTC
uint32 time = 6; // seconds since 1970
}
// a data message to forward to an external app (or possibly also be consumed internally in the case of CLEAR_TEXT and CLEAR_READACK
@ -192,6 +195,9 @@ message RadioConfig {
// Send our owner info at least this often (also we always send once at boot - to rejoin the mesh)
uint32 send_owner_secs = 2;
/// If we miss this many owner messages from a node, we declare the node offline (defaults to 3 - to allow for some lost packets)
uint32 num_missed_to_fail = 3;
// If true, radio should not try to be smart about what packets to queue to the phone
bool keep_all_packets = 100;
@ -229,11 +235,9 @@ SET_CONFIG (switches device to a new set of radio params and preshared key, drop
message NodeInfo {
int32 num = 1; // the node number
User user = 2;
Position position = 3;
// Times are typically not sent over the mesh, but they will be added to any Packet (chain of SubPacket)
// sent to the phone (so the phone can know exact time of reception)
uint32 last_seen = 4; // seconds since 1970
/// This position data will also contain a time last seen
Position position = 3;
/// Returns the Signal-to-noise ratio (SNR) of the last received message, as measured
/// by the receiver.
@ -295,10 +299,10 @@ message DeviceState {
/// We bump up the integer values in this enum to indicate minimum levels of encodings for saved files
/// if your file is below the Minimum you should discard it.
Minimum = 13;
Minimum = 15;
/// The current value we are using for saved files
Current = 13;
Current = 15;
};
/// A version integer used to invalidate old save files when we make incompatible changes