use only async io when talking to the radio

pull/42/head
geeksville 2020-06-11 16:22:20 -07:00
rodzic 73c2c8def1
commit e20f7c5943
2 zmienionych plików z 54 dodań i 38 usunięć

Wyświetl plik

@ -12,7 +12,6 @@ import com.geeksville.concurrent.handledLaunch
import com.geeksville.util.anonymize
import com.geeksville.util.exceptionReporter
import com.geeksville.util.ignoreException
import com.geeksville.util.toRemoteExceptions
import kotlinx.coroutines.delay
import java.lang.reflect.Method
import java.util.*
@ -208,21 +207,31 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
}
}
/// Send a packet/command out the radio link
override fun handleSendToRadio(p: ByteArray) {
try {
debug("sending to radio")
doWrite(
BTM_TORADIO_CHARACTER,
p
) // Do a synchronous write, so that we can then do our reads if needed
if (isFirstSend) {
isFirstSend = false
doReadFromRadio(false)
/// Send a packet/command out the radio link
override fun handleSendToRadio(a: ByteArray) {
safe?.let { s ->
val uuid = BTM_TORADIO_CHARACTER
debug("queuing ${a.size} bytes to $uuid")
// Note: we generate a new characteristic each time, because we are about to
// change the data and we want the data stored in the closure
val toRadio = getCharacteristic(uuid)
toRadio.value = a
s.asyncWriteCharacteristic(toRadio) { r ->
try {
r.getOrThrow()
debug("write of ${a.size} bytes completed")
if (isFirstSend) {
isFirstSend = false
doReadFromRadio(false)
}
} catch (ex: Exception) {
errormsg("Ignoring sendToRadio exception: $ex")
}
}
} catch (ex: Exception) {
errormsg("Ignoring sendToRadio exception: $ex")
}
}
@ -248,9 +257,8 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
startWatchingFromNum()
}
} catch (ex: BLEException) {
errormsg(
"error during doReadFromRadio",
ex
warn(
"error during doReadFromRadio - disconnecting, ${ex.message}"
)
service.serviceScope.handledLaunch { retryDueToException() }
}
@ -274,10 +282,21 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
/// We only force service refresh the _first_ time we connect to the device. Thereafter it is assumed the firmware didn't change
private var hasForcedRefresh = false
@Volatile
var fromNumChanged = false
private fun startWatchingFromNum() {
safe!!.setNotify(fromNum, true) {
debug("fromNum changed, so we are reading new messages")
doReadFromRadio(false)
// We might get multiple notifies before we get around to reading from the radio - so just set one flag
fromNumChanged = true
debug("fromNum changed")
service.serviceScope.handledLaunch {
if (fromNumChanged) {
fromNumChanged = false
debug("fromNum changed, so we are reading new messages")
doReadFromRadio(false)
}
}
}
}
@ -309,9 +328,11 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
}
/// We only try to set MTU once, because some buggy implementations fail
@Volatile
private var shouldSetMtu = true
/// For testing
@Volatile
private var isFirstTime = true
private fun doDiscoverServicesAndInit() {
@ -410,22 +431,6 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
lostConnectCb = { service.onDisconnect(isPermanent = false) })
}
/**
* do a synchronous write operation
*/
private fun doWrite(uuid: UUID, a: ByteArray) = toRemoteExceptions {
safe?.let { s ->
debug("queuing ${a.size} bytes to $uuid")
// Note: we generate a new characteristic each time, because we are about to
// change the data and we want the data stored in the closure
val toRadio = getCharacteristic(uuid)
toRadio.value = a
s.writeCharacteristic(toRadio)
debug("write of ${a.size} bytes completed")
}
}
/**
* Get a chracteristic, but in a safe manner because some buggy BLE implementations might return null

Wyświetl plik

@ -58,12 +58,18 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
/// Users can access the GATT directly as needed
var gatt: BluetoothGatt? = null
@Volatile
var state = BluetoothProfile.STATE_DISCONNECTED
@Volatile
private var currentWork: BluetoothContinuation? = null
private val workQueue = mutableListOf<BluetoothContinuation>()
// Called for reconnection attemps
@Volatile
private var connectionCallback: ((Result<Unit>) -> Unit)? = null
@Volatile
private var lostConnectCallback: (() -> Unit)? = null
/// from characteristic UUIDs to the handler function for notfies
@ -123,13 +129,17 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
debug("Starting work: $tag")
return startWorkFn()
}
override fun toString(): String {
return super.toString()
}
}
/**
* skanky hack to restart BLE if it says it is hosed
* https://stackoverflow.com/questions/35103701/ble-android-onconnectionstatechange-not-being-called
*/
var mHandler: Handler = Handler()
private val mHandler: Handler = Handler()
fun restartBle() {
GeeksvilleApplication.analytics.track("ble_restart") // record # of times we needed to use this nasty hack
@ -533,7 +543,8 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
lostConnectCb: () -> Unit
) {
logAssert(workQueue.isEmpty())
logAssert(currentWork == null) // I don't think anything should be able to sneak in front
if (currentWork != null)
throw AssertionError("currentWork was not null: $currentWork")
lostConnectCallback = lostConnectCb
connectionCallback = if (autoConnect)
@ -663,7 +674,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
// Cancel any notifications - because when the device comes back it might have forgotten about us
notifyHandlers.clear()
ignoreException {
// Hmm - sometimes the "Connection closing" exception comes back to us - ignore it
failAllWork(BLEException("Connection closing"))