BLEComm: Added callbacks to enable handling of notifications in calling class

This commit is contained in:
jbr7rr 2023-02-23 21:05:50 +01:00
parent f31f6c1649
commit ed100a4eb0
3 changed files with 103 additions and 34 deletions

View file

@ -0,0 +1,19 @@
package info.nightscout.pump.medtrum.comm
class ReadDataPacket(data: ByteArray) {
private var totalData = data.copyOfRange(0, data.size - 1) // Strip crc
private var dataSize: Byte = data[0]
fun addData(newData: ByteArray) {
totalData += newData.copyOfRange(4, newData.size - 1) // Strip header and crc
}
fun allDataReceived(): Boolean {
return (totalData.size >= dataSize)
}
fun getData(): ByteArray {
return totalData
}
}

View file

@ -32,9 +32,9 @@ import info.nightscout.interfaces.pump.PumpSync
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.pump.medtrum.extension.toInt
import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.comm.WriteCommandPackets
import info.nightscout.pump.medtrum.comm.ManufacturerData
import info.nightscout.pump.medtrum.comm.ReadDataPacket
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventDismissNotification
import info.nightscout.rx.events.EventPumpStatusChanged
@ -48,6 +48,14 @@ import java.util.Arrays
import javax.inject.Inject
import javax.inject.Singleton
interface BLECommCallback {
open fun onBLEConnected() {}
open fun onNotification(notification: ByteArray) {}
open fun onIndication(indication: ByteArray) {}
open fun onSendMessageError(reason: String)
}
@Singleton
class BLEComm @Inject internal constructor(
private val injector: HasAndroidInjector,
@ -74,23 +82,28 @@ class BLEComm @Inject internal constructor(
private const val NEEDS_ENABLE = 0x30
private const val MANUFACTURER_ID = 18305
private const val COMMAND_AUTH_REQ: Byte = 5
}
private val handler =
Handler(HandlerThread(this::class.simpleName + "Handler").also { it.start() }.looper)
private val mBluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter
private var mBluetoothGatt: BluetoothGatt? = null
private val mCrypt = Crypt()
var isConnected = false
var isConnecting = false
private var uartWrite: BluetoothGattCharacteristic? = null
private var uartRead: BluetoothGattCharacteristic? = null
private var mDeviceSN: Long = 0
// Read and write buffers
private var mWritePackets = WriteCommandPackets()
private var mReadPacket: ReadDataPacket? = null
private var mDeviceSN: Long = 0
private var mCallback: BLECommCallback? = null
fun setCallback(callback: BLECommCallback?) {
this.mCallback = callback
}
/** Connect flow: 1. Start scanning for our device (SN entered in settings) */
@SuppressLint("MissingPermission")
@ -225,9 +238,21 @@ class BLEComm @Inject internal constructor(
override fun onCharacteristicChanged(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
aapsLogger.debug(LTag.PUMPBTCOMM, "onCharacteristicChanged")
// TODO: Make split between Notif and Indication
// Notif contains pump status and alarms
readDataParsing(characteristic.value)
val value = characteristic.getValue()
if (characteristic.getUuid() == UUID.fromString(READ_UUID)) {
mCallback?.onNotification(value)
} else if (characteristic.getUuid() == UUID.fromString(WRITE_UUID)) {
if (mReadPacket == null) {
mReadPacket = ReadDataPacket(value)
} else {
mReadPacket?.addData(value)
}
if (mReadPacket?.allDataReceived() == true) {
mReadPacket = null
mReadPacket?.getData()?.let { mCallback?.onIndication(it) }
}
}
}
override fun onCharacteristicWrite(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, status: Int) {
@ -242,7 +267,7 @@ class BLEComm @Inject internal constructor(
}
}
} else {
// TODO: What to do here?
mCallback?.onSendMessageError("onCharacteristicWrite failure")
}
}
@ -303,7 +328,10 @@ class BLEComm @Inject internal constructor(
}
if (notificationEnabled) {
aapsLogger.debug(LTag.PUMPBTCOMM, "Notifications enabled!")
authorize()
/** Connect flow: 6. Connected */
mCallback?.onBLEConnected()
isConnected = true
isConnecting = false
}
}
}
@ -351,23 +379,6 @@ class BLEComm @Inject internal constructor(
}
}
private fun readDataParsing(receivedData: ByteArray) {
aapsLogger.debug(LTag.PUMPBTCOMM, "<<<<< readDataParsing " + Arrays.toString(receivedData))
// TODO Implement
// TODO place this at the correct place
/** Connect flow: 6. Authorized */
isConnected = true
isConnecting = false
}
private fun authorize() {
aapsLogger.debug(LTag.PUMPBTCOMM, "Start auth!")
val role = 2 // Fixed to 2 for pump
val key = mCrypt.keyGen(mDeviceSN)
val commandData = byteArrayOf(COMMAND_AUTH_REQ) + byteArrayOf(role.toByte()) + 0.toByteArray(4) + key.toByteArray(4)
sendMessage(commandData)
}
fun sendMessage(message: ByteArray) {
aapsLogger.debug(LTag.PUMPBTCOMM, "sendMessage message = " + Arrays.toString(message))
if (!mWritePackets.allPacketsConsumed()) {
@ -381,6 +392,7 @@ class BLEComm @Inject internal constructor(
writeCharacteristic(uartWriteBTGattChar, value)
} else {
aapsLogger.error(LTag.PUMPBTCOMM, "sendMessage error in writePacket!")
mCallback?.onSendMessageError("error in writePacket!")
}
}
}

View file

@ -23,6 +23,8 @@ import info.nightscout.interfaces.queue.Command
import info.nightscout.interfaces.queue.CommandQueue
import info.nightscout.interfaces.ui.UiInteraction
import info.nightscout.pump.medtrum.MedtrumPlugin
import info.nightscout.pump.medtrum.encryption.Crypt
import info.nightscout.pump.medtrum.extension.toByteArray
import info.nightscout.rx.AapsSchedulers
import info.nightscout.rx.bus.RxBus
import info.nightscout.rx.events.EventAppExit
@ -40,12 +42,13 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.joda.time.DateTime
import org.joda.time.DateTimeZone
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.min
class MedtrumService : DaggerService() {
class MedtrumService : DaggerService(), BLECommCallback {
@Inject lateinit var injector: HasAndroidInjector
@Inject lateinit var aapsLogger: AAPSLogger
@ -65,11 +68,19 @@ class MedtrumService : DaggerService() {
@Inject lateinit var pumpSync: PumpSync
@Inject lateinit var dateUtil: DateUtil
companion object {
private const val COMMAND_AUTH_REQ: Byte = 5
}
private val disposable = CompositeDisposable()
private val mBinder: IBinder = LocalBinder()
private val mCrypt = Crypt()
private var mDeviceSN: Long = 0
override fun onCreate() {
super.onCreate()
bleComm.setCallback(this)
disposable += rxBus
.toObservable(EventAppExit::class.java)
.observeOn(aapsSchedulers.io)
@ -89,22 +100,20 @@ class MedtrumService : DaggerService() {
fun connect(from: String, deviceSN: Long): Boolean {
// TODO Check we might want to replace this with start scan?
mDeviceSN = deviceSN
return bleComm.connect(from, deviceSN)
}
fun stopConnecting() {
// TODO proper way for this might need send commands etc
bleComm.stopConnecting()
}
fun disconnect(from: String) {
// TODO proper way for this might need send commands etc
bleComm.disconnect(from)
}
fun sendMessage(message: ByteArray) { // TODO Check what we use here?
// TODO
bleComm.sendMessage(message)
}
fun readPumpStatus() {
// TODO
}
@ -179,6 +188,35 @@ class MedtrumService : DaggerService() {
return false
}
private fun authorize() {
aapsLogger.debug(LTag.PUMPCOMM, "Start auth!")
val role = 2 // Fixed to 2 for pump
val key = mCrypt.keyGen(mDeviceSN)
val commandData = byteArrayOf(COMMAND_AUTH_REQ) + byteArrayOf(role.toByte()) + 0.toByteArray(4) + key.toByteArray(4)
bleComm.sendMessage(commandData)
}
/** BLECommCallbacks */
override fun onBLEConnected() {
// TODO Replace by FSM Entry?
authorize()
}
override fun onNotification(notification: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onNotification" + notification.contentToString())
// TODO
}
override fun onIndication(indication: ByteArray) {
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< onIndication" + indication.contentToString())
// TODO
}
override fun onSendMessageError(reason: String) {
aapsLogger.debug(LTag.PUMPCOMM, "<<<<< error during send message " + reason)
// TODO
}
/** Service stuff */
inner class LocalBinder : Binder() {
val serviceInstance: MedtrumService