From 3027c2ffa54b4899b49e480da0711251084852f1 Mon Sep 17 00:00:00 2001 From: jbr7rr <> Date: Thu, 25 May 2023 13:32:24 +0200 Subject: [PATCH] Initial bolus implementation, further connection improvements --- pump/medtrum/build.gradle | 1 + .../nightscout/pump/medtrum/MedtrumPlugin.kt | 44 ++++++++++- .../nightscout/pump/medtrum/MedtrumPump.kt | 32 +++++++- .../pump/medtrum/comm/enums/BolusType.kt | 12 +++ .../medtrum/comm/packets/AuthorizePacket.kt | 8 -- .../medtrum/comm/packets/GetRecordPacket.kt | 48 ++++++++++++ .../comm/packets/NotificationPacket.kt | 7 +- .../pump/medtrum/services/BLEComm.kt | 33 ++++---- .../pump/medtrum/services/MedtrumService.kt | 75 +++++++++++++++---- .../ui/viewmodel/MedtrumOverviewViewModel.kt | 13 ++++ .../res/layout/fragment_medtrum_overview.xml | 48 ++++++++++++ pump/medtrum/src/main/res/values/strings.xml | 7 +- 12 files changed, 279 insertions(+), 49 deletions(-) create mode 100644 pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/BolusType.kt diff --git a/pump/medtrum/build.gradle b/pump/medtrum/build.gradle index 6ceef5fce7..6d951eafa6 100644 --- a/pump/medtrum/build.gradle +++ b/pump/medtrum/build.gradle @@ -27,6 +27,7 @@ dependencies { implementation project(':core:main') implementation project(':core:ui') implementation project(':core:validators') + implementation project(':pump:pump-common') implementation project(':core:utils') testImplementation project(':core:main') diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt index 0d2870fe36..6b7fe84598 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPlugin.kt @@ -8,12 +8,15 @@ import android.os.IBinder import dagger.android.HasAndroidInjector import info.nightscout.core.ui.toast.ToastUtils import info.nightscout.core.utils.fabric.FabricPrivacy +import info.nightscout.interfaces.constraints.Constraint +import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.notifications.Notification import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.profile.Profile import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.pump.DetailedBolusInfo +import info.nightscout.interfaces.pump.DetailedBolusInfoStorage import info.nightscout.interfaces.pump.Pump import info.nightscout.interfaces.pump.PumpEnactResult import info.nightscout.interfaces.pump.PumpPluginBase @@ -52,6 +55,7 @@ import org.json.JSONException import org.json.JSONObject import javax.inject.Inject import javax.inject.Singleton +import kotlin.math.abs import kotlin.math.round @Singleton class MedtrumPlugin @Inject constructor( @@ -59,6 +63,7 @@ import kotlin.math.round aapsLogger: AAPSLogger, rh: ResourceHelper, commandQueue: CommandQueue, + private val constraintChecker: Constraints, private val sp: SP, private val aapsSchedulers: AapsSchedulers, private val rxBus: RxBus, @@ -69,6 +74,7 @@ import kotlin.math.round private val uiInteraction: UiInteraction, private val profileFunction: ProfileFunction, private val pumpSync: PumpSync, + private val detailedBolusInfoStorage: DetailedBolusInfoStorage, private val temporaryBasalStorage: TemporaryBasalStorage ) : PumpPluginBase( PluginDescription() @@ -204,7 +210,7 @@ import kotlin.math.round } override fun lastDataTime(): Long { - return medtrumPump.lastTimeReceivedFromPump * 1000L + return medtrumPump.lastTimeReceivedFromPump } override val baseBasalRate: Double @@ -216,14 +222,46 @@ import kotlin.math.round override val batteryLevel: Int get() = 0 // TODO + @Synchronized override fun deliverTreatment(detailedBolusInfo: DetailedBolusInfo): PumpEnactResult { - return PumpEnactResult(injector) // TODO + aapsLogger.debug(LTag.PUMP, "deliverTreatment: " + detailedBolusInfo.insulin + "U") + if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false) + detailedBolusInfo.insulin = constraintChecker.applyBolusConstraints(Constraint(detailedBolusInfo.insulin)).value() + return if (detailedBolusInfo.insulin > 0 && detailedBolusInfo.carbs == 0.0) { + aapsLogger.debug(LTag.PUMP, "deliverTreatment: Delivering bolus: " + detailedBolusInfo.insulin + "U") + detailedBolusInfoStorage.add(detailedBolusInfo) // will be picked up on reading history + val t = EventOverviewBolusProgress.Treatment(0.0, 0, detailedBolusInfo.bolusType == DetailedBolusInfo.BolusType.SMB, detailedBolusInfo.id) + val connectionOK = medtrumService?.setBolus(detailedBolusInfo.insulin, t) ?: false + val result = PumpEnactResult(injector) + result.success = connectionOK && abs(detailedBolusInfo.insulin - t.insulin) < pumpDescription.bolusStep + result.bolusDelivered = t.insulin + if (!result.success) { + // Todo error code? + result.comment = "error" + } else { + result.comment = "ok" + } + aapsLogger.debug(LTag.PUMP, "deliverTreatment: OK. Success: ${result.success} Asked: ${detailedBolusInfo.insulin} Delivered: ${result.bolusDelivered}") + result + } else { + aapsLogger.debug(LTag.PUMP, "deliverTreatment: Invalid input") + val result = PumpEnactResult(injector) + result.success = false + result.bolusDelivered = 0.0 + result.comment = rh.gs(info.nightscout.core.ui.R.string.invalid_input) + aapsLogger.error("deliverTreatment: Invalid input") + result + } } override fun stopBolusDelivering() { - // TODO + if (!isInitialized()) return + + aapsLogger.info(LTag.PUMP, "stopBolusDelivering") + medtrumService?.stopBolus() } + @Synchronized override fun setTempBasalAbsolute(absoluteRate: Double, durationInMinutes: Int, profile: Profile, enforceNew: Boolean, tbrType: PumpSync.TemporaryBasalType): PumpEnactResult { if (!isInitialized()) return PumpEnactResult(injector).success(false).enacted(false) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt index bcc4d77733..0feda438df 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/MedtrumPump.kt @@ -12,6 +12,7 @@ import info.nightscout.pump.medtrum.comm.enums.BasalType import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toInt +import info.nightscout.rx.events.EventOverviewBolusProgress import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag import info.nightscout.shared.sharedPreferences.SP @@ -66,7 +67,15 @@ class MedtrumPump @Inject constructor( set(value) { _primeProgress.value = value } - + + private val _lastBasalRate = MutableStateFlow(0.0) + val lastBasalRateFlow: StateFlow = _lastBasalRate + var lastBasalRate: Double + get() = _lastBasalRate.value + set(value) { + _lastBasalRate.value = value + } + /** Stuff stored in SP */ private var _patchSessionToken = 0L var patchSessionToken: Long @@ -136,9 +145,17 @@ class MedtrumPump @Inject constructor( var alarmFlags = 0 var alarmParameter = 0 + // bolus status + var bolusingTreatment: EventOverviewBolusProgress.Treatment? = null // actually delivered treatment + var bolusAmountToBeDelivered = 0.0 // amount to be delivered + var bolusProgressLastTimeStamp: Long = 0 // timestamp of last bolus progress message + var bolusStopped = false // bolus finished + var bolusStopForced = false // bolus forced to stop by user + var bolusDone = false // success end + // Last basal status update // TODO: Save this in SP? - var lastBasalRate = 0.0 + var lastBasalSequence = 0 var lastBasalPatchId = 0L var lastBasalStartTime = 0L @@ -270,6 +287,17 @@ class MedtrumPump @Inject constructor( handleBasalStatusUpdate(basalType, basalValue, basalSequence, basalPatchId, basalStartTime, dateUtil.now()) } + fun handleBolusStatusUpdate(bolusType: Int, bolusCompleted: Boolean, amountDelivered: Double) { + aapsLogger.debug(LTag.PUMP, "handleBolusStatusUpdate: bolusType: $bolusType bolusCompleted: $bolusCompleted amountDelivered: $amountDelivered") + bolusProgressLastTimeStamp = dateUtil.now() + if (bolusCompleted) { + bolusDone = true + bolusingTreatment?.insulin = amountDelivered + } else { + bolusingTreatment?.insulin = amountDelivered + } + } + fun handleBasalStatusUpdate(basalType: BasalType, basalRate: Double, basalSequence: Int, basalPatchId: Long, basalStartTime: Long, receivedTime: Long) { aapsLogger.debug( LTag.PUMP, diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/BolusType.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/BolusType.kt new file mode 100644 index 0000000000..77634d5fb6 --- /dev/null +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/enums/BolusType.kt @@ -0,0 +1,12 @@ +package info.nightscout.pump.medtrum.comm.enums + +enum class BolusType { + NONE, + NORMAL, + EXTEND, + COMBINATION; + + fun getValue(): Int { + return ordinal + } +} diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt index 5125de09fa..2093ae8cf7 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/AuthorizePacket.kt @@ -41,14 +41,6 @@ class AuthorizePacket(injector: HasAndroidInjector) : MedtrumPacket(injector) { } override fun handleResponse(data: ByteArray): Boolean { - if (data.size > 3) { - val incomingOpCode: Byte = data.copyOfRange(RESP_OPCODE_START, RESP_OPCODE_END).first() - if (incomingOpCode == CommandType.SUBSCRIBE.code) { - // TODO: Test and see if this can be removed - aapsLogger.error(LTag.PUMPCOMM, "handleResponse: Got subscribe response instead of authorize response, handling subscribe packet") - return SubscribePacket(injector).handleResponse(data) - } - } val success = super.handleResponse(data) if (success) { deviceType = data.copyOfRange(RESP_DEVICE_TYPE_START, RESP_DEVICE_TYPE_END).toInt() diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetRecordPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetRecordPacket.kt index 0f45e405b8..78eabeca96 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetRecordPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/GetRecordPacket.kt @@ -1,11 +1,13 @@ package info.nightscout.pump.medtrum.comm.packets import dagger.android.HasAndroidInjector +import info.nightscout.interfaces.pump.DetailedBolusInfoStorage import info.nightscout.interfaces.pump.PumpSync import info.nightscout.interfaces.pump.TemporaryBasalStorage import info.nightscout.pump.medtrum.MedtrumPump import info.nightscout.pump.medtrum.comm.enums.CommandType.GET_RECORD import info.nightscout.pump.medtrum.comm.enums.BasalType +import info.nightscout.pump.medtrum.comm.enums.BolusType import info.nightscout.pump.medtrum.extension.toByteArray import info.nightscout.pump.medtrum.extension.toInt import info.nightscout.pump.medtrum.extension.toLong @@ -20,6 +22,7 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int @Inject lateinit var medtrumPump: MedtrumPump @Inject lateinit var pumpSync: PumpSync @Inject lateinit var temporaryBasalStorage: TemporaryBasalStorage + @Inject lateinit var detailedBolusInfoStorage: DetailedBolusInfoStorage @Inject lateinit var dateUtil: DateUtil companion object { @@ -85,6 +88,51 @@ class GetRecordPacket(injector: HasAndroidInjector, private val recordIndex: Int when (recordType) { BOLUS_RECORD, BOLUS_RECORD_ALT -> { aapsLogger.debug(LTag.PUMPCOMM, "GetRecordPacket HandleResponse: BOLUS_RECORD") + val typeAndWizard = data.copyOfRange(RESP_RECORD_DATA_START, RESP_RECORD_DATA_START + 1).toInt() + val bolusCause = data.copyOfRange(RESP_RECORD_DATA_START + 1, RESP_RECORD_DATA_START + 2).toInt() + val unknown = data.copyOfRange(RESP_RECORD_DATA_START + 2, RESP_RECORD_DATA_START + 4).toInt() + val bolusStartTime = MedtrumTimeUtil().convertPumpTimeToSystemTimeMillis(data.copyOfRange(RESP_RECORD_DATA_START + 4, RESP_RECORD_DATA_START + 8).toLong()) + val bolusNormalAmount = data.copyOfRange(RESP_RECORD_DATA_START + 8, RESP_RECORD_DATA_START + 10).toInt() * 0.05 + val bolusNormalDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 10, RESP_RECORD_DATA_START + 12).toInt() * 0.05 + val bolusExtendedAmount = data.copyOfRange(RESP_RECORD_DATA_START + 12, RESP_RECORD_DATA_START + 14).toInt() * 0.05 + val bolusExtendedDuration = data.copyOfRange(RESP_RECORD_DATA_START + 14, RESP_RECORD_DATA_START + 16).toInt() + val bolusExtendedDelivered = data.copyOfRange(RESP_RECORD_DATA_START + 16, RESP_RECORD_DATA_START + 18).toInt() * 0.05 + val bolusCarb = data.copyOfRange(RESP_RECORD_DATA_START + 18, RESP_RECORD_DATA_START + 20).toInt() + val bolusGlucose = data.copyOfRange(RESP_RECORD_DATA_START + 20, RESP_RECORD_DATA_START + 22).toInt() + val bolusIOB = data.copyOfRange(RESP_RECORD_DATA_START + 22, RESP_RECORD_DATA_START + 24).toInt() + val unkown1 = data.copyOfRange(RESP_RECORD_DATA_START + 24, RESP_RECORD_DATA_START + 26).toInt() + val unkown2 = data.copyOfRange(RESP_RECORD_DATA_START + 26, RESP_RECORD_DATA_START + 28).toInt() + val bolusType = enumValues()[typeAndWizard and 0x0F] + val bolusWizard = (typeAndWizard and 0xF0) != 0 + aapsLogger.debug( + LTag.PUMPCOMM, + "GetRecordPacket HandleResponse: BOLUS_RECORD: typeAndWizard: $typeAndWizard, bolusCause: $bolusCause, unknown: $unknown, bolusStartTime: $bolusStartTime, " + "bolusNormalAmount: $bolusNormalAmount, bolusNormalDelivered: $bolusNormalDelivered, bolusExtendedAmount: $bolusExtendedAmount, bolusExtendedDuration: $bolusExtendedDuration, " + "bolusExtendedDelivered: $bolusExtendedDelivered, bolusCarb: $bolusCarb, bolusGlucose: $bolusGlucose, bolusIOB: $bolusIOB, unkown1: $unkown1, unkown2: $unkown2, " + "bolusType: $bolusType, bolusWizard: $bolusWizard" + ) + if (bolusType == BolusType.NORMAL) { + val detailedBolusInfo = detailedBolusInfoStorage.findDetailedBolusInfo(bolusStartTime, bolusNormalDelivered) + val newRecord = pumpSync.syncBolusWithPumpId( + timestamp = bolusStartTime, + amount = bolusNormalDelivered, + type = detailedBolusInfo?.bolusType, + pumpId = bolusStartTime, + pumpType = medtrumPump.pumpType, + pumpSerial = medtrumPump.pumpSN.toString(radix = 16) + ) + aapsLogger.debug( + LTag.PUMPCOMM, + "from record: ${if (newRecord) "**NEW** " else ""}EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) Bolus: ${bolusNormalDelivered}U " + ) + if (!newRecord && detailedBolusInfo != null) { + // detailedInfo can be from another similar record. Reinsert + detailedBolusInfoStorage.add(detailedBolusInfo) + } + } else { + aapsLogger.error( + LTag.PUMPCOMM, + "from record: EVENT BOLUS ${dateUtil.dateAndTimeString(bolusStartTime)} ($bolusStartTime) " + "Bolus type: $bolusType not supported" + ) + } + } BASAL_RECORD, BASAL_RECORD_ALT -> { diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt index ef03a67f71..fd53d32bb0 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/comm/packets/NotificationPacket.kt @@ -98,13 +98,10 @@ class NotificationPacket(val injector: HasAndroidInjector) { aapsLogger.debug(LTag.PUMPCOMM, "Normal bolus notification received") var bolusData = data.copyOfRange(offset, offset + 1).toInt() var bolusType = bolusData and 0x7F - var bolusCompleted = (bolusData shr 7) and 0x01 // TODO: Check for other flags here :) + val bolusCompleted: Boolean = ((bolusData shr 7) and 0x01) != 0 // TODO: Check for other flags here :) var bolusDelivered = data.copyOfRange(offset + 1, offset + 3).toInt() * 0.05 - // TODO Sync bolus flow: - // If bolus is known add status - // If bolus is not known start read bolus - // When bolus is completed, remove bolus from medtrumPump aapsLogger.debug(LTag.PUMPCOMM, "Bolus type: $bolusType, bolusData: $bolusData bolus completed: $bolusCompleted, bolus delivered: $bolusDelivered") + medtrumPump.handleBolusStatusUpdate(bolusType, bolusCompleted, bolusDelivered) offset += 3 } diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt index 12c23b15db..8ee0910383 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/BLEComm.kt @@ -90,8 +90,8 @@ class BLEComm @Inject internal constructor( private val mBluetoothAdapter: BluetoothAdapter? get() = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager?)?.adapter private var mBluetoothGatt: BluetoothGatt? = null - var isConnected = false // TODO: These may be removed have no function - var isConnecting = false// TODO: These may be removed have no function + private var isConnected = false // Only to track internal ble state + private var isConnecting = false // Only to track internal ble state private var uartWrite: BluetoothGattCharacteristic? = null private var uartRead: BluetoothGattCharacteristic? = null @@ -126,14 +126,12 @@ class BLEComm @Inject internal constructor( isConnected = false - // TODO: Maybe replace this by (or add) a isScanning parameter? isConnecting = true // Find our Medtrum Device! filters.add( ScanFilter.Builder().setDeviceName("MT").build() ) - // TODO Check if we need to add MAC for reconnects? Not sure if otherwise we can find the device mBluetoothAdapter?.bluetoothLeScanner?.startScan(filters, settings, mScanCallback) return true } @@ -159,6 +157,9 @@ class BLEComm @Inject internal constructor( return false } + // TODO: THIS IS A WORKAROUND TEST + mWritePackets = WriteCommandPackets() + if (mDevice != null && mDeviceSN == deviceSN) { // Skip scanning and directly connect to gatt aapsLogger.debug(LTag.PUMPBTCOMM, "Skipping scan and directly connecting to gatt") @@ -178,7 +179,7 @@ class BLEComm @Inject internal constructor( /** Connect flow: 2. When device is found this is called by onScanResult() */ @SuppressLint("MissingPermission") @Synchronized - fun connectGatt(device: BluetoothDevice) { + private fun connectGatt(device: BluetoothDevice) { mBluetoothGatt = device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE) } @@ -196,12 +197,14 @@ class BLEComm @Inject internal constructor( if (isConnecting) { stopScan() } - mBluetoothGatt?.disconnect() - } - - @Synchronized - fun stopConnecting() { - isConnecting = false + if (isConnected) { + mBluetoothGatt?.disconnect() + } else { + close() + isConnected = false + isConnecting = false + mCallback?.onBLEDisconnected() + } } @SuppressLint("MissingPermission") @@ -347,8 +350,6 @@ class BLEComm @Inject internal constructor( aapsLogger.debug(LTag.PUMPBTCOMM, "Notifications enabled!") /** Connect flow: 6. Connected */ mCallback?.onBLEConnected() - isConnected = true - isConnecting = false } } } @@ -384,9 +385,9 @@ class BLEComm @Inject internal constructor( private fun onConnectionStateChangeSynchronized(gatt: BluetoothGatt, status: Int, newState: Int) { aapsLogger.debug(LTag.PUMPBTCOMM, "onConnectionStateChange newState: " + newState + " status: " + status) if (newState == BluetoothProfile.STATE_CONNECTED) { - handler.postDelayed({ - mBluetoothGatt?.discoverServices() - }, WRITE_DELAY_MILLIS) + isConnected = true + isConnecting = false + mBluetoothGatt?.discoverServices() } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { close() isConnected = false diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt index 23533c744f..8ab48a512e 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/services/MedtrumService.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.os.Binder import android.os.IBinder +import android.os.SystemClock import dagger.android.DaggerService import dagger.android.HasAndroidInjector import info.nightscout.core.utils.fabric.FabricPrivacy @@ -14,6 +15,7 @@ import info.nightscout.interfaces.profile.Profile import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.interfaces.pump.PumpEnactResult import info.nightscout.interfaces.pump.PumpSync +import info.nightscout.interfaces.queue.Callback import info.nightscout.interfaces.queue.CommandQueue import info.nightscout.interfaces.ui.UiInteraction import info.nightscout.pump.medtrum.MedtrumPlugin @@ -42,6 +44,7 @@ import io.reactivex.rxjava3.kotlin.plusAssign import java.util.* import javax.inject.Inject import kotlin.math.abs +import kotlin.math.round class MedtrumService : DaggerService(), BLECommCallback { @@ -130,12 +133,10 @@ class MedtrumService : DaggerService(), BLECommCallback { } fun stopConnecting() { - // TODO proper way for this might need send commands etc - bleComm.stopConnecting() + bleComm.disconnect("stopConnecting") } fun disconnect(from: String) { - // TODO proper way for this might need send commands etc bleComm.disconnect(from) } @@ -155,14 +156,58 @@ class MedtrumService : DaggerService(), BLECommCallback { } } - fun bolus(insulin: Double, carbs: Int, carbTime: Long, t: EventOverviewBolusProgress.Treatment): Boolean { + fun setBolus(insulin: Double, t: EventOverviewBolusProgress.Treatment): Boolean { if (!isConnected) return false - // TODO - return false + val result = sendPacketAndGetResponse(SetBolusPacket(injector, insulin)) + + medtrumPump.bolusDone = false + medtrumPump.bolusingTreatment = t + medtrumPump.bolusAmountToBeDelivered = insulin + medtrumPump.bolusStopped = false + medtrumPump.bolusStopForced = false + medtrumPump.bolusProgressLastTimeStamp = dateUtil.now() + + val bolusStart = System.currentTimeMillis() + + val bolusingEvent = EventOverviewBolusProgress + while (medtrumPump.bolusStopped == false && result == true && medtrumPump.bolusDone == false) { + SystemClock.sleep(100) + if (System.currentTimeMillis() - medtrumPump.bolusProgressLastTimeStamp > T.secs(15).msecs()) { + medtrumPump.bolusStopped = true + medtrumPump.bolusStopForced = true + aapsLogger.debug(LTag.PUMPCOMM, "Communication stopped") + bleComm.disconnect("Communication stopped") + } else { + bolusingEvent.t = medtrumPump.bolusingTreatment + bolusingEvent.status = rh.gs(info.nightscout.pump.common.R.string.bolus_delivered_so_far, medtrumPump.bolusingTreatment?.insulin, medtrumPump.bolusAmountToBeDelivered) + bolusingEvent.percent = round((medtrumPump.bolusingTreatment?.insulin?.div(medtrumPump.bolusAmountToBeDelivered) ?: 0.0) * 100).toInt() - 1 + rxBus.send(bolusingEvent) + } + } + + bolusingEvent.t = medtrumPump.bolusingTreatment + bolusingEvent.percent = 99 + medtrumPump.bolusingTreatment = null + + val bolusDurationInMSec = (insulin * 60 * 1000) + val expectedEnd = bolusStart + bolusDurationInMSec + 2000 + while (System.currentTimeMillis() < expectedEnd) { + SystemClock.sleep(1000) + } + + // Do not call update status directly, reconnection may be needed + commandQueue.readStatus(rh.gs(info.nightscout.pump.medtrum.R.string.gettingbolusstatus), object : Callback() { + override fun run() { + rxBus.send(EventPumpStatusChanged(rh.gs(info.nightscout.pump.medtrum.R.string.gettingbolusstatus))) + bolusingEvent.percent = 100 + } + }) + return result } - fun bolusStop() { - // TODO + fun stopBolus() { + var result = sendPacketAndGetResponse(CancelBolusPacket(injector)) + aapsLogger.debug(LTag.PUMPCOMM, "bolusStop: result: $result") } fun setTempBasal(absoluteRate: Double, durationInMinutes: Int): Boolean { @@ -190,12 +235,15 @@ class MedtrumService : DaggerService(), BLECommCallback { } fun updateBasalsInPump(profile: Profile): Boolean { - var result = false - val packet = medtrumPump.buildMedtrumProfileArray(profile)?.let { SetBasalProfilePacket(injector, it) } - result = packet?.let { sendPacketAndGetResponse(it) } == true - - // TODO: We might want to get rid of this and cancel the TBR before we set the basal profile + var result = true // Update basal affects the TBR records (the pump will cancel the TBR, set our basal profile, and resume the TBR in a new record) + // Cancel any TBR in progress + if (medtrumPump.tempBasalInProgress) { + result = sendPacketAndGetResponse(CancelTempBasalPacket(injector)) + } + val packet = medtrumPump.buildMedtrumProfileArray(profile)?.let { SetBasalProfilePacket(injector, it) } + if (result) result = packet?.let { sendPacketAndGetResponse(it) } == true + // Get history records, this will update the pump state and add changes in TBR to AAPS history if (result) result = syncRecords() @@ -211,7 +259,6 @@ class MedtrumService : DaggerService(), BLECommCallback { private fun syncRecords(): Boolean { aapsLogger.debug(LTag.PUMP, "syncRecords: called!, syncedSequenceNumber: ${medtrumPump.syncedSequenceNumber}, currentSequenceNumber: ${medtrumPump.currentSequenceNumber}") var result = false - // TODO: Check if we need to sync older records as well // Note: medtrum app fetches all records when they sync? for (sequence in medtrumPump.syncedSequenceNumber..medtrumPump.currentSequenceNumber) { result = sendPacketAndGetResponse(GetRecordPacket(injector, sequence)) diff --git a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt index e1f1c74ecb..26f1a57c15 100644 --- a/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt +++ b/pump/medtrum/src/main/java/info/nightscout/pump/medtrum/ui/viewmodel/MedtrumOverviewViewModel.kt @@ -10,6 +10,7 @@ import info.nightscout.pump.medtrum.ui.event.UIEvent import info.nightscout.pump.medtrum.ui.viewmodel.BaseViewModel import info.nightscout.interfaces.profile.ProfileFunction import info.nightscout.pump.medtrum.MedtrumPump +import info.nightscout.pump.medtrum.R import info.nightscout.pump.medtrum.code.ConnectionState import info.nightscout.pump.medtrum.comm.enums.MedtrumPumpState import info.nightscout.rx.AapsSchedulers @@ -17,6 +18,7 @@ import info.nightscout.rx.bus.RxBus import info.nightscout.rx.events.EventPumpStatusChanged import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.LTag +import info.nightscout.shared.interfaces.ResourceHelper import io.reactivex.rxjava3.disposables.CompositeDisposable import io.reactivex.rxjava3.kotlin.plusAssign import kotlinx.coroutines.CoroutineScope @@ -27,6 +29,7 @@ import javax.inject.Inject class MedtrumOverviewViewModel @Inject constructor( private val aapsLogger: AAPSLogger, + private val rh: ResourceHelper, private val rxBus: RxBus, private val aapsSchedulers: AapsSchedulers, private val fabricPrivacy: FabricPrivacy, @@ -48,6 +51,10 @@ class MedtrumOverviewViewModel @Inject constructor( val isPatchActivated: LiveData get() = _isPatchActivated + private val _runningBasalRate = SingleLiveEvent() + val runningBasalRate: LiveData + get() = _runningBasalRate + init { scope.launch { medtrumPump.connectionStateFlow.collect { state -> @@ -77,6 +84,12 @@ class MedtrumOverviewViewModel @Inject constructor( } } } + scope.launch { + medtrumPump.lastBasalRateFlow.collect { rate -> + aapsLogger.debug(LTag.PUMP, "MedtrumViewModel runningBasalRateFlow: $rate") + _runningBasalRate.postValue(String.format(rh.gs(R.string.current_basal_rate), rate)) + } + } } override fun onCleared() { diff --git a/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml b/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml index 68c7073fad..c557a6cd04 100644 --- a/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml +++ b/pump/medtrum/src/main/res/layout/fragment_medtrum_overview.xml @@ -91,6 +91,54 @@ + + + + + + + + + + + diff --git a/pump/medtrum/src/main/res/values/strings.xml b/pump/medtrum/src/main/res/values/strings.xml index d987e70c7a..1388653576 100644 --- a/pump/medtrum/src/main/res/values/strings.xml +++ b/pump/medtrum/src/main/res/values/strings.xml @@ -12,12 +12,14 @@ synced_sequence_number Medtrum - MEDTRUM + MT Medtrum Nano Medtrum pump settings BLE Status + Active basal + %.2f U/h Start new patch Stop patch @@ -40,5 +42,8 @@ SN Serial number pump base + Waiting for bolus end. Remaining %1$d sec. + Getting bolus status +