fix: Threading & optimizations

This commit is contained in:
Piotr Dec 2024-08-01 22:52:26 +02:00
parent 6d71245d9f
commit f5ec500343
Signed by: stawros
GPG key ID: F89F27AD8F881A91
4 changed files with 142 additions and 142 deletions

View file

@ -7,12 +7,11 @@ import android.bluetooth.BluetoothSocket
import android.util.Log import android.util.Log
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
import eu.ztsh.garmin.data.DataCache import eu.ztsh.garmin.data.DataCache
import eu.ztsh.garmin.data.GarminManeuver
import eu.ztsh.garmin.data.GarminMapper import eu.ztsh.garmin.data.GarminMapper
import eu.ztsh.garmin.data.GarminModelItem
import eu.ztsh.garmin.data.MapboxMapper import eu.ztsh.garmin.data.MapboxMapper
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
import java.util.concurrent.Executors
import java.util.concurrent.SynchronousQueue import java.util.concurrent.SynchronousQueue
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
@ -23,88 +22,98 @@ class Garmin(
) { ) {
private lateinit var connection: ConnectThread private lateinit var connection: ConnectThread
private val observer = ProcessingThreadObserver() private lateinit var maneuvers: ManeuverProcessingThread
private val cache = DataCache() private val cache = DataCache()
private val maneuversPool = Executors.newFixedThreadPool(4)
fun start() { fun start() {
connection = ConnectThread() connection = ConnectThread()
connection.start() connection.start()
maneuvers = ManeuverProcessingThread()
maneuvers.start()
} }
fun close() { fun close() {
connection.close() connection.close()
maneuvers.interrupt()
maneuvers.join(0)
maneuversPool.shutdown()
} }
fun process(maneuver: Maneuver) { fun process(maneuver: Maneuver) {
ManeuverProcessingThread(maneuver).start() maneuversPool.submit{maneuvers.enqueue(maneuver)}
} }
// fun process(location: LocationMatcherResult) { private inner class ManeuverProcessingThread : ProcessingThread<Maneuver>() {
// LocationProcessingThread(location).start()
// }
// fun process(navigationSessionState: NavigationSessionState) { override fun mapAndSend(maybeItem: Maneuver?): Maneuver? {
// cache.update(navigationSessionState) if (maybeItem != null) {
// } Log.d(TAG, "mapAndSend (${currentThread().name}): got new")
var changed = false
private inner class ManeuverProcessingThread(val maneuver: Maneuver) : ProcessingThread<GarminManeuver>() { if (cache.hasChanged(maybeItem.laneGuidance)) {
changed = true
override fun process(): GarminManeuver? { Log.d(TAG, "mapAndSend: lanes")
if (cache.hasChanged(maneuver)) { send(GarminMapper.map(MapboxMapper.asLanes(maybeItem)))
cache.update(maneuver) }
return MapboxMapper.map(maneuver) if (cache.hasChanged(maybeItem.stepDistance)) {
changed = true
Log.d(TAG, "mapAndSend: stepDistance")
send(GarminMapper.map(MapboxMapper.asDistance(maybeItem)))
}
if (cache.hasChanged(maybeItem.primary)) {
changed = true
Log.d(TAG, "mapAndSend: primary")
send(GarminMapper.map(MapboxMapper.asDirection(maybeItem)))
}
if (changed) {
return maybeItem
}
} }
return null return null
} }
override fun enqueue(item: GarminManeuver) { override fun updateCache(item: Maneuver) {
if (cache.hasChanged(item.lanes)) { cache.update(item)
connection.enqueue(GarminMapper.map(item.lanes))
}
if (cache.hasChanged(item.direction)) {
connection.enqueue(GarminMapper.map(item.direction))
}
if (cache.hasChanged(item.distance)) {
connection.enqueue(GarminMapper.map(item.distance))
}
// flag?
} }
} }
// private inner class LocationProcessingThread(val location: LocationMatcherResult) : ProcessingThread() { private abstract inner class ProcessingThread<T> : Thread() {
//
// override fun process() {
// if (cache.hasChanged(location)) {
// cache.update(location)
// send(MapboxMapper.apply(location))
// }
// }
// }
private abstract inner class ProcessingThread<T : GarminModelItem> : Thread() { private val queue: SynchronousQueue<T> = SynchronousQueue()
private var stop: Boolean = false
abstract fun process(): T? abstract fun mapAndSend(maybeItem: T?): T?
abstract fun enqueue(item: T) abstract fun updateCache(item: T)
fun send(data: IntArray) {
connection.enqueue(data)
}
fun enqueue(item: T) {
queue.put(item)
}
override fun run() { override fun run() {
val processing = process() while (!stop) {
if (processing != null) { val maybeItem = queue.poll()
enqueue(processing) val item = mapAndSend(maybeItem)
cache.update(processing) if (item != null) {
Log.d(TAG, "run: Cache updated")
updateCache(item)
}
} }
observer.join(this)
} }
} override fun interrupt() {
stop = true
private inner class ProcessingThreadObserver { super.interrupt()
fun <T : GarminModelItem> join(thread: ProcessingThread<T>) {
thread.join()
} }
} }
private inner class ConnectThread : Thread() { private inner class ConnectThread : Thread() {
@ -126,9 +135,11 @@ class Garmin(
context.setConnectionStatus(true) context.setConnectionStatus(true)
sleep(3000) sleep(3000)
readAll() readAll()
send(intArrayOf(0x07, 0x01)) // Set GPS to true
while (true) { while (true) {
val newCurrent = queue.poll() val newCurrent = queue.poll()
if (newCurrent == null) { if (newCurrent == null) {
Log.d(TAG, "run (${currentThread().name}): Sleep...")
sleep(500) sleep(500)
} else { } else {
current = newCurrent current = newCurrent
@ -177,7 +188,6 @@ class Garmin(
private fun sendRaw(buff: IntArray) { private fun sendRaw(buff: IntArray) {
buff.forEach { socket!!.outputStream.write(it) } buff.forEach { socket!!.outputStream.write(it) }
socket!!.outputStream.flush() socket!!.outputStream.flush()
sleep(2000)
readAll() readAll()
} }

View file

@ -2,7 +2,10 @@ package eu.ztsh.garmin.data
import com.mapbox.navigation.core.trip.session.LocationMatcherResult import com.mapbox.navigation.core.trip.session.LocationMatcherResult
import com.mapbox.navigation.core.trip.session.NavigationSessionState import com.mapbox.navigation.core.trip.session.NavigationSessionState
import com.mapbox.navigation.tripdata.maneuver.model.Lane
import com.mapbox.navigation.tripdata.maneuver.model.Maneuver import com.mapbox.navigation.tripdata.maneuver.model.Maneuver
import com.mapbox.navigation.tripdata.maneuver.model.PrimaryManeuver
import com.mapbox.navigation.tripdata.maneuver.model.StepDistance
class DataCache { class DataCache {
@ -11,38 +14,17 @@ class DataCache {
private var locationCache: LocationMatcherResult? = null private var locationCache: LocationMatcherResult? = null
private var session: NavigationSessionState? = null private var session: NavigationSessionState? = null
// state
fun hasChanged(lanes: Lanes): Boolean {
return garminManeuver.lanes != lanes
}
fun hasChanged(distance: Distance): Boolean {
return garminManeuver.distance != distance
}
fun hasChanged(direction: Direction): Boolean {
return garminManeuver.direction != direction
}
//
// fun hasChanged(speed: Speed?): Boolean {
// return stateCache.speed == null || stateCache.speed != speed
// }
//
// fun hasChanged(arrival: Arrival?): Boolean {
// return stateCache.arrival == null || stateCache.arrival != arrival
// }
// Merge states
fun update(item: GarminModelItem) {
when(item) {
is GarminManeuver -> garminManeuver.merge(item)
}
}
// maneuver // maneuver
fun hasChanged(maneuver: Maneuver): Boolean { fun hasChanged(guidance: Lane?): Boolean {
return maneuverCache == null || maneuverCache!! != maneuver return guidance != null && maneuverCache.let { it == null || it.laneGuidance != guidance }
}
fun hasChanged(distance: StepDistance): Boolean {
return maneuverCache.let { it == null || it.stepDistance != distance }
}
fun hasChanged(primaryManeuver: PrimaryManeuver): Boolean {
return maneuverCache.let { it == null || it.primary != primaryManeuver }
} }
fun update(maneuver: Maneuver) { fun update(maneuver: Maneuver) {

View file

@ -6,74 +6,78 @@ import com.mapbox.navigation.core.trip.session.LocationMatcherResult
class MapboxMapper { class MapboxMapper {
companion object { companion object {
fun map(maneuver: Maneuver): GarminManeuver { fun asDirection(maneuver: Maneuver): Direction {
val state = GarminManeuver() val direction = Direction()
maneuver.apply { maneuver.primary.apply {
this.primary.apply { when (this.type) {
state.direction = Direction() "roundabout" -> {
when (this.type) { direction.out = OutType.RightRoundabout
"roundabout" -> {
state.direction.out = OutType.RightRoundabout
}
"fork" -> state.direction.out = OutType.LongerLane
"arrive" -> {
state.flag = true
}
"turn" -> {
when (this.type) {
"straight" -> state.direction.angle = OutAngle.Straight
}
}
} }
when (this.modifier) { "fork" -> direction.out = OutType.LongerLane
"right" -> { "arrive" -> {
when (this.type) { // flag = true
"turn" -> state.direction.angle = OutAngle.Right }
"roundabout" -> { "turn" -> {
when (this.degrees) { when (this.type) {
137.0 -> state.direction.angle = OutAngle.EasyRight "straight" -> direction.angle = OutAngle.Straight
180.0 -> state.direction.angle = OutAngle.Straight
}
}
"off ramp" -> {
state.direction.angle = OutAngle.EasyRight
state.direction.out = OutType.LongerLane
}
}
}
"left" -> {
when (this.type) {
"turn" -> state.direction.angle = OutAngle.Left
}
} }
} }
} }
this.stepDistance.apply { when (this.modifier) {
this.distanceRemaining?.apply { "right" -> {
distanceFormatter.formatDistance(this).split(" ").apply { when (this.type) {
state.distance = Distance( "turn" -> direction.angle = OutAngle.Right
this[0].replace(',', '.').toDouble(), "roundabout" -> {
when (this[1]) { when (this.degrees) {
"m" -> Unit.Metres 137.0 -> direction.angle = OutAngle.EasyRight
"km" -> Unit.Kilometres 180.0 -> direction.angle = OutAngle.Straight
else -> Unit.Any
} }
) }
"off ramp" -> {
direction.angle = OutAngle.EasyRight
direction.out = OutType.LongerLane
}
} }
} }
}
// TODO: implement "left" -> {
state.lanes = Lanes(Arrows(listOf()), Arrows(listOf())) when (this.type) {
this.laneGuidance?.apply { "turn" -> direction.angle = OutAngle.Left
this.allLanes.apply { "off ramp" -> {
println() direction.angle = OutAngle.EasyLeft
direction.out = OutType.LongerLane
}
}
} }
} }
} }
return state return direction
}
fun asDistance(maneuver: Maneuver): Distance {
return maneuver.stepDistance.let { step ->
step.distanceFormatter.formatDistance(step.distanceRemaining!!).split(" ").let {
Distance(
it[0].replace(',', '.').toDouble(),
when (it[1]) {
"m" -> Unit.Metres
"km" -> Unit.Kilometres
else -> Unit.Any
}
)
}
}
}
fun asLanes(maneuver: Maneuver): Lanes {
// TODO: implement
maneuver.laneGuidance?.apply {
this.allLanes.apply {
println()
}
}
return Lanes(Arrows(listOf()), Arrows(listOf()))
} }
fun map(locationMatcherResult: LocationMatcherResult): GarminLocation { fun map(locationMatcherResult: LocationMatcherResult): GarminLocation {

View file

@ -89,6 +89,10 @@ class Distance(val distance: Double, val unit: Unit) {
return result return result
} }
override fun toString(): String {
return "Distance($distance$unit)"
}
} }
class Speed(val speed: Int, val limit: Int) class Speed(val speed: Int, val limit: Int)