kotlin a bit more idiomatic after conversion from Java

This commit is contained in:
AdrianLxM 2021-02-24 20:12:29 +01:00 committed by Andrei Vereha
parent 647793e0b7
commit 2a14d60bee
11 changed files with 78 additions and 127 deletions

View file

@ -1,68 +1,49 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm
import javax.inject.Singleton
import javax.inject.Inject
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashCommunicationManager
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import info.nightscout.androidaps.logging.AAPSLogger
import android.bluetooth.BluetoothGatt
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO
import kotlin.Throws
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.ScanFailException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.FailedToConnectException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotSendBleException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.BleIOBusyException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWrite
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotEnableNotifications
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.DescriptorNotFoundException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
import android.bluetooth.BluetoothDevice
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.BleManager
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ServiceDiscoverer
import android.bluetooth.BluetoothGattCharacteristic
import android.content.Context
import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.BuildConfig
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks.BleCommCallbacks
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.command.BleCommandHello
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.*
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.PodScanner
import java.util.*
import java.util.concurrent.BlockingQueue
import java.util.concurrent.LinkedBlockingDeque
import java.util.concurrent.TimeoutException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BleManager @Inject constructor(private val context: Context) : OmnipodDashCommunicationManager {
class BleManager @Inject constructor(private val context: Context, private val aapsLogger: AAPSLogger) : OmnipodDashCommunicationManager {
private val bluetoothAdapter: BluetoothAdapter
private val bluetoothManager: BluetoothManager
private val bluetoothManager: BluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
@Inject lateinit var aapsLogger: AAPSLogger
private var podAddress: String? = null
private var gatt: BluetoothGatt? = null
private var bleio: BleIO? = null
@Throws(InterruptedException::class, ScanFailException::class, FailedToConnectException::class, CouldNotSendBleException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWrite::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
fun activateNewPod() {
aapsLogger.info(LTag.PUMPBTCOMM, "starting new pod activation")
val podScanner = PodScanner(aapsLogger, bluetoothAdapter)
podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).scanResult.device.address
val podAddress = podScanner.scanForPod(PodScanner.SCAN_FOR_SERVICE_UUID, PodScanner.POD_ID_NOT_ACTIVATED).scanResult.device.address
// For tests: this.podAddress = "B8:27:EB:1D:7E:BB";
connect()
connect(podAddress)
}
@Throws(FailedToConnectException::class, CouldNotSendBleException::class, InterruptedException::class, BleIOBusyException::class, TimeoutException::class, CouldNotConfirmWrite::class, CouldNotEnableNotifications::class, DescriptorNotFoundException::class, CouldNotConfirmDescriptorWriteException::class)
fun connect() {
private fun connect(podAddress: String) {
// TODO: locking?
val podDevice = bluetoothAdapter.getRemoteDevice(podAddress)
var incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>> =
mapOf(CharacteristicType.CMD to LinkedBlockingDeque(),
CharacteristicType.DATA to LinkedBlockingDeque());
val bleCommCallbacks = BleCommCallbacks(aapsLogger, incomingPackets)
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to " + podAddress)
aapsLogger.debug(LTag.PUMPBTCOMM, "Connecting to $podAddress")
var autoConnect = true
if (BuildConfig.DEBUG) {
autoConnect = false
@ -70,8 +51,6 @@ class BleManager @Inject constructor(private val context: Context) : OmnipodDash
// it's easier to start testing from scratch on each run.
}
val gatt = podDevice.connectGatt(context, autoConnect, bleCommCallbacks, BluetoothDevice.TRANSPORT_LE)
this.gatt = gatt
bleCommCallbacks.waitForConnection(CONNECT_TIMEOUT_MS)
val connectionState = bluetoothManager.getConnectionState(podDevice, BluetoothProfile.GATT)
aapsLogger.debug(LTag.PUMPBTCOMM, "GATT connection state: $connectionState")
@ -80,10 +59,10 @@ class BleManager @Inject constructor(private val context: Context) : OmnipodDash
}
val discoverer = ServiceDiscoverer(aapsLogger, gatt, bleCommCallbacks)
val chars = discoverer.discoverServices()
bleio = BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks)
val bleIO = BleIO(aapsLogger, chars, incomingPackets, gatt, bleCommCallbacks)
aapsLogger.debug(LTag.PUMPBTCOMM, "Saying hello to the pod")
bleio!!.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).asByteArray())
bleio!!.readyToRead()
bleIO.sendAndConfirmPacket(CharacteristicType.CMD, BleCommandHello(CONTROLLER_ID).asByteArray())
bleIO.readyToRead()
}
companion object {
@ -92,8 +71,4 @@ class BleManager @Inject constructor(private val context: Context) : OmnipodDash
private const val CONTROLLER_ID = 4242 // TODO read from preferences or somewhere else.
}
init {
bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter
}
}

View file

@ -14,13 +14,9 @@ enum class CharacteristicType(val value: String) {
companion object {
@JvmStatic fun byValue(value: String): CharacteristicType {
for (type in values()) {
if (type.value == value) {
return type
}
}
throw IllegalArgumentException("Unknown Characteristic Type: $value")
}
@JvmStatic
fun byValue(value: String): CharacteristicType =
values().firstOrNull { it.value == value }
?: throw IllegalArgumentException("Unknown Characteristic Type: $value")
}
}

View file

@ -22,26 +22,25 @@ class ServiceDiscoverer(private val logger: AAPSLogger, private val gatt: Blueto
gatt.discoverServices()
bleCallbacks.waitForServiceDiscovery(DISCOVER_SERVICES_TIMEOUT_MS)
logger.debug(LTag.PUMPBTCOMM, "Services discovered")
val service = gatt.getService(
uuidFromString(SERVICE_UUID))
val service = gatt.getService(SERVICE_UUID.toUuid())
?: throw ServiceNotFoundException(SERVICE_UUID)
val cmdChar = service.getCharacteristic(CharacteristicType.CMD.uUID)
?: throw CharacteristicNotFoundException(CharacteristicType.CMD.value)
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uUID)
val dataChar = service.getCharacteristic(CharacteristicType.DATA.uUID) // TODO: this is never used
?: throw CharacteristicNotFoundException(CharacteristicType.DATA.value)
var chars = mapOf(CharacteristicType.CMD to cmdChar,
CharacteristicType.DATA to dataChar)
return chars
}
private fun String.toUuid(): UUID = UUID(
BigInteger(replace("-", "").substring(0, 16), 16).toLong(),
BigInteger(replace("-", "").substring(16), 16).toLong()
)
companion object {
private const val SERVICE_UUID = "1a7e-4024-e3ed-4464-8b7e-751e03d0dc5f"
private const val DISCOVER_SERVICES_TIMEOUT_MS = 5000
private fun uuidFromString(s: String): UUID {
return UUID(
BigInteger(s.replace("-", "").substring(0, 16), 16).toLong(),
BigInteger(s.replace("-", "").substring(16), 16).toLong()
)
}
}
}

View file

@ -11,21 +11,19 @@ import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.Characte
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.CharacteristicType.Companion.byValue
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmDescriptorWriteException
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions.CouldNotConfirmWrite
import java.util.*
import java.util.concurrent.BlockingQueue
import java.util.concurrent.CountDownLatch
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
class BleCommCallbacks(aapsLogger: AAPSLogger, incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>) : BluetoothGattCallback() {
class BleCommCallbacks(private val aapsLogger: AAPSLogger, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>) : BluetoothGattCallback() {
private val serviceDiscoveryComplete: CountDownLatch = CountDownLatch(1)
private val connected: CountDownLatch = CountDownLatch(1)
private val writeQueue: BlockingQueue<CharacteristicWriteConfirmation> = LinkedBlockingQueue(1)
private val descriptorWriteQueue: BlockingQueue<DescriptorWriteConfirmation> = LinkedBlockingQueue(1)
private val serviceDiscoveryComplete: CountDownLatch
private val connected: CountDownLatch
private val aapsLogger: AAPSLogger
private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>
private val writeQueue: BlockingQueue<CharacteristicWriteConfirmation>
private val descriptorWriteQueue: BlockingQueue<DescriptorWriteConfirmation>
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
aapsLogger.debug(LTag.PUMPBTCOMM, "OnConnectionStateChange discovered with status/state$status/$newState")
@ -42,42 +40,53 @@ class BleCommCallbacks(aapsLogger: AAPSLogger, incomingPackets: Map<Characterist
}
}
@Throws(InterruptedException::class) fun waitForConnection(timeout_ms: Int) {
@Throws(InterruptedException::class)
fun waitForConnection(timeout_ms: Int) {
connected.await(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
}
@Throws(InterruptedException::class) fun waitForServiceDiscovery(timeout_ms: Int) {
@Throws(InterruptedException::class)
fun waitForServiceDiscovery(timeout_ms: Int) {
serviceDiscoveryComplete.await(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
}
@Throws(InterruptedException::class, TimeoutException::class, CouldNotConfirmWrite::class)
fun confirmWrite(expectedPayload: ByteArray, timeout_ms: Int) {
val received = writeQueue.poll(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
val received: CharacteristicWriteConfirmation = writeQueue.poll(timeout_ms.toLong(), TimeUnit.MILLISECONDS)
?: throw TimeoutException()
if (!Arrays.equals(expectedPayload, received.payload)) {
when (received) {
is CharacteristicWriteConfirmationPayload -> confirmWritePayload(expectedPayload, received)
is CharacteristicWriteConfirmationError ->
aapsLogger.debug(LTag.PUMPBTCOMM, "Could not confirm write: status was ${received.status}")
}
}
private fun confirmWritePayload(expectedPayload: ByteArray, received: CharacteristicWriteConfirmationPayload) {
if (!expectedPayload.contentEquals(received.payload)) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Could not confirm write. Got " + received.payload + ".Excepted: " + expectedPayload + ". Status: " + received.status)
throw CouldNotConfirmWrite(expectedPayload, received.payload!!)
throw CouldNotConfirmWrite(expectedPayload, received.payload)
}
aapsLogger.debug(LTag.PUMPBTCOMM, "Confirmed write with value: " + received.payload)
}
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
var received: ByteArray? = null
if (status == BluetoothGatt.GATT_SUCCESS) {
received = characteristic.value
val writeConfirmation = if (status == BluetoothGatt.GATT_SUCCESS) {
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite value " + characteristic.getStringValue(0))
CharacteristicWriteConfirmationPayload(characteristic.value, status)
} else {
CharacteristicWriteConfirmationError(status)
}
aapsLogger.debug(LTag.PUMPBTCOMM, "OnCharacteristicWrite with status/char/value " +
status + "/" +
byValue(characteristic.uuid.toString()) + "/" +
received)
status + "/" + byValue(characteristic.uuid.toString()) + "/" + characteristic.value)
try {
if (writeQueue.size > 0) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Write confirm queue should be empty. found: " + writeQueue.size)
writeQueue.clear()
}
val offered = writeQueue.offer(CharacteristicWriteConfirmation(received, status), WRITE_CONFIRM_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
val offered = writeQueue.offer(writeConfirmation, WRITE_CONFIRM_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
if (!offered) {
aapsLogger.warn(LTag.PUMPBTCOMM, "Received delayed write confirmation")
}
@ -130,13 +139,4 @@ class BleCommCallbacks(aapsLogger: AAPSLogger, incomingPackets: Map<Characterist
private const val WRITE_CONFIRM_TIMEOUT_MS = 10 // the other thread should be waiting for the exchange
}
init {
serviceDiscoveryComplete = CountDownLatch(1)
connected = CountDownLatch(1)
this.aapsLogger = aapsLogger
this.incomingPackets = incomingPackets
writeQueue = LinkedBlockingQueue(1)
descriptorWriteQueue = LinkedBlockingQueue(1)
}
}

View file

@ -1,3 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.callbacks
class CharacteristicWriteConfirmation(var payload: ByteArray?, var status: Int)
sealed class CharacteristicWriteConfirmation
class CharacteristicWriteConfirmationPayload(val payload: ByteArray, val status: Int) : CharacteristicWriteConfirmation()
class CharacteristicWriteConfirmationError(val status: Int) : CharacteristicWriteConfirmation()

View file

@ -5,13 +5,8 @@ enum class BleCommandType(val value: Byte) {
companion object {
fun byValue(value: Byte): BleCommandType {
for (type in values()) {
if (type.value == value) {
return type
}
}
throw IllegalArgumentException("Unknown BleCommandType: $value")
}
fun byValue(value: Byte): BleCommandType =
BleCommandType.values().firstOrNull { it.value == value }
?: throw IllegalArgumentException("Unknown BleCommandType: $value")
}
}

View file

@ -1,10 +1,3 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.exceptions
class CouldNotConfirmWrite(private val sent: ByteArray, confirmed: ByteArray) : Exception() {
private val confirmed: Any
init {
this.confirmed = confirmed
}
}
class CouldNotConfirmWrite(private val sent: ByteArray, private val confirmed: ByteArray?) : Exception()

View file

@ -3,13 +3,9 @@ package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.excepti
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.scan.BleDiscoveredDevice
import java.util.*
class ScanFailFoundTooManyException(devices: List<BleDiscoveredDevice>?) : ScanFailException() {
class ScanFailFoundTooManyException(devices: List<BleDiscoveredDevice>) : ScanFailException() {
private val devices: List<BleDiscoveredDevice>
private val devices: List<BleDiscoveredDevice> = ArrayList(devices)
val discoveredDevices: List<BleDiscoveredDevice>
get() = Collections.unmodifiableList(devices)
init {
this.devices = ArrayList(devices)
}
}

View file

@ -14,7 +14,7 @@ import java.util.concurrent.TimeoutException
class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<CharacteristicType, BluetoothGattCharacteristic>, private val incomingPackets: Map<CharacteristicType, BlockingQueue<ByteArray>>, private val gatt: BluetoothGatt, private val bleCommCallbacks: BleCommCallbacks) {
private var state: IOState
private var state: IOState = IOState.IDLE
/***
*
@ -29,7 +29,7 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
}
state = IOState.READING
}
val ret = incomingPackets[characteristic]!!.poll(DEFAULT_IO_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
val ret = incomingPackets[characteristic]?.poll(DEFAULT_IO_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
?: throw TimeoutException()
synchronized(state) { state = IOState.IDLE }
return ret
@ -97,8 +97,4 @@ class BleIO(private val aapsLogger: AAPSLogger, private val chars: Map<Character
private const val DEFAULT_IO_TIMEOUT_MS = 1000
}
init {
state = IOState.IDLE
}
}

View file

@ -31,7 +31,7 @@ class PodScanner(private val logger: AAPSLogger, private val bluetoothAdapter: B
scanner.flushPendingScanResults(scanCollector)
scanner.stopScan(scanCollector)
val collected = scanCollector.collect()
if (collected.size == 0) {
if (collected.isEmpty()) {
throw ScanFailNotFoundException()
} else if (collected.size > 1) {
throw ScanFailFoundTooManyException(collected)

View file

@ -12,7 +12,7 @@ import java.util.concurrent.ConcurrentHashMap
class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : ScanCallback() {
// there could be different threads calling the onScanResult callback
private val found: ConcurrentHashMap<String, ScanResult>
private val found: ConcurrentHashMap<String, ScanResult> = ConcurrentHashMap()
private var scanFailed = 0
override fun onScanResult(callbackType: Int, result: ScanResult) {
// callbackType will be ALL
@ -31,7 +31,7 @@ class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : S
if (scanFailed != 0) {
throw ScanFailException(scanFailed)
}
logger.debug(LTag.PUMPBTCOMM, "ScanCollector looking for podID: " + podID)
logger.debug(LTag.PUMPBTCOMM, "ScanCollector looking for podID: $podID")
for (result in found.values) {
try {
val device = BleDiscoveredDevice(result, podID)
@ -45,7 +45,4 @@ class ScanCollector(private val logger: AAPSLogger, private val podID: Long) : S
return Collections.unmodifiableList(ret)
}
init {
found = ConcurrentHashMap()
}
}