From 863fb19ff057665bec70ddaa71b1e66bcb5597a7 Mon Sep 17 00:00:00 2001 From: Andrei Vereha Date: Sat, 19 Jun 2021 13:13:43 +0200 Subject: [PATCH] fix bolus cancelation --- .../omnipod/dash/OmnipodDashPumpPlugin.kt | 112 +++++++++++------- .../driver/comm/OmnipodDashBleManagerImpl.kt | 2 +- .../driver/pod/definition/DeliveryStatus.kt | 12 ++ .../pod/state/OmnipodDashPodStateManager.kt | 8 +- .../state/OmnipodDashPodStateManagerImpl.kt | 10 +- .../dash/ui/OmnipodDashOverviewFragment.kt | 2 +- 6 files changed, 95 insertions(+), 51 deletions(-) diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt index 023d0318d5..8eaf7f4c7c 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/OmnipodDashPumpPlugin.kt @@ -17,6 +17,7 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType import info.nightscout.androidaps.plugins.pump.omnipod.common.definition.OmnipodCommandType import info.nightscout.androidaps.plugins.pump.omnipod.common.queue.command.* import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.OmnipodDashManager +import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManager import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.ActivationProgress import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.BeepType import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.pod.definition.DeliveryStatus @@ -56,6 +57,7 @@ class OmnipodDashPumpPlugin @Inject constructor( private val pumpSync: PumpSync, private val rxBus: RxBusWrapper, private val aapsSchedulers: AapsSchedulers, + private val bleManager: OmnipodDashBleManager, // private val disposable: CompositeDisposable = CompositeDisposable(), // private val aapsSchedulers: AapsSchedulers, @@ -68,6 +70,8 @@ class OmnipodDashPumpPlugin @Inject constructor( @Volatile var bolusCanceled = false companion object { + private const val BOLUS_RETRY_INTERVAL_MS = 2000.toLong() + private const val BOLUS_RETRIES = 5 // numer of retries for cancel/get bolus status private val pluginDescription = PluginDescription() .mainType(PluginType.PUMP) @@ -96,6 +100,7 @@ class OmnipodDashPumpPlugin @Inject constructor( } override fun isConnected(): Boolean { + return true } @@ -163,9 +168,9 @@ class OmnipodDashPumpPlugin @Inject constructor( ) } podStateManager.lastBolus?.run { - if (!complete) { + if (!deliveryComplete) { val deliveredUnits = markComplete() - complete = true + deliveryComplete = true val bolusHistoryEntry = history.getById(historyId) val sync = pumpSync.syncBolusWithPumpId( timestamp = bolusHistoryEntry.createdAt, @@ -293,11 +298,11 @@ class OmnipodDashPumpPlugin @Inject constructor( if (detailedBolusInfo.carbs > 0 || detailedBolusInfo.insulin == 0.0 ) { + // Accept only valid insulin requests return PumpEnactResult(injector) .success(false) .enacted(false) .bolusDelivered(0.0) - .carbsDelivered(0.0) .comment("Invalid input") } val requestedBolusAmount = detailedBolusInfo.insulin @@ -306,7 +311,6 @@ class OmnipodDashPumpPlugin @Inject constructor( .success(false) .enacted(false) .bolusDelivered(0.0) - .carbsDelivered(0.0) .comment("Not enough insulin in the reservoir") } var deliveredBolusAmount = 0.0 @@ -337,7 +341,7 @@ class OmnipodDashPumpPlugin @Inject constructor( ).filter { podEvent -> podEvent.isCommandSent() } .map { pumpSyncBolusStart(requestedBolusAmount, detailedBolusInfo.bolusType) } .ignoreElements(), - post = waitForBolusDeliveryToComplete(5, requestedBolusAmount, detailedBolusInfo.bolusType) + post = waitForBolusDeliveryToComplete(BOLUS_RETRIES, requestedBolusAmount, detailedBolusInfo.bolusType) .map { deliveredBolusAmount = it aapsLogger.info(LTag.PUMP, "deliverTreatment: deliveredBolusAmount=$deliveredBolusAmount") @@ -346,7 +350,10 @@ class OmnipodDashPumpPlugin @Inject constructor( ).toSingleDefault( PumpEnactResult(injector).success(true).enacted(true).bolusDelivered(deliveredBolusAmount) ) - .onErrorReturnItem(PumpEnactResult(injector).success(false).enacted(false)) + .onErrorReturnItem( + // success if canceled + PumpEnactResult(injector).success(bolusCanceled).enacted(false) + ) .blockingGet() aapsLogger.info(LTag.PUMP, "deliverTreatment result: $ret") return ret @@ -363,12 +370,37 @@ class OmnipodDashPumpPlugin @Inject constructor( } } + private fun updateBolusProgressDialog(msg: String, percent: Int) { + val progressUpdateEvent = EventOverviewBolusProgress + val percent = percent + progressUpdateEvent.status = msg + progressUpdateEvent.percent = percent + rxBus.send(progressUpdateEvent) + } + private fun waitForBolusDeliveryToComplete( maxRetries: Int, requestedBolusAmount: Double, bolusType: DetailedBolusInfo.BolusType ): Single = Single.defer { - // wait for bolus delivery confirmation, if possible + + if (bolusCanceled && podStateManager.activeCommand != null) { + var errorGettingStatus: Throwable? = null + for (tries in 1..maxRetries) { + errorGettingStatus = getPodStatus().blockingGet() + if (errorGettingStatus != null) { + aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus") + Thread.sleep(BOLUS_RETRY_INTERVAL_MS) // retry every 2 sec + continue + } + } + if (errorGettingStatus != null) { + // requestedBolusAmount will be updated later, via pumpSync + // This state is: cancellation requested and getPodStatus failed 5 times. + return@defer Single.just(requestedBolusAmount) + } + } + val estimatedDeliveryTimeSeconds = estimateBolusDeliverySeconds(requestedBolusAmount) aapsLogger.info(LTag.PUMP, "estimatedDeliveryTimeSeconds: $estimatedDeliveryTimeSeconds") var waited = 0 @@ -378,43 +410,53 @@ class OmnipodDashPumpPlugin @Inject constructor( if (bolusType == DetailedBolusInfo.BolusType.SMB) { continue } - val progressUpdateEvent = EventOverviewBolusProgress - val percent = (100 * waited) / estimatedDeliveryTimeSeconds - progressUpdateEvent.status = resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount) - progressUpdateEvent.percent = percent.toInt() - rxBus.send(progressUpdateEvent) + val percent = waited.toFloat() / estimatedDeliveryTimeSeconds + updateBolusProgressDialog(resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount), percent.toInt()) } for (tries in 1..maxRetries) { - val errorGettingStatus = getPodStatus().blockingGet() + val cmd = if (bolusCanceled) + cancelBolus() + else + getPodStatus() + + val errorGettingStatus = cmd.blockingGet() if (errorGettingStatus != null) { - Thread.sleep(3000) // retry every 3 sec + aapsLogger.debug(LTag.PUMP, "waitForBolusDeliveryToComplete errorGettingStatus=$errorGettingStatus") + Thread.sleep(BOLUS_RETRY_INTERVAL_MS) // retry every 3 sec continue } - val bolusInDeliveryState = - podStateManager.deliveryStatus in - arrayOf( - DeliveryStatus.BOLUS_AND_TEMP_BASAL_ACTIVE, - DeliveryStatus.BOLUS_AND_BASAL_ACTIVE - ) + val bolusDeliveringActive = podStateManager.deliveryStatus?.bolusDeliveringActive() ?: false - if (bolusInDeliveryState && !bolusCanceled) { + if (bolusDeliveringActive) { // delivery not complete yet val remainingUnits = podStateManager.lastBolus!!.bolusUnitsRemaining - val progressUpdateEvent = EventOverviewBolusProgress val percent = ((requestedBolusAmount - remainingUnits) / requestedBolusAmount) * 100 - progressUpdateEvent.status = "Remaining: $remainingUnits units" - progressUpdateEvent.percent = percent.toInt() - Thread.sleep(estimateBolusDeliverySeconds(remainingUnits) * 1000.toLong()) + updateBolusProgressDialog( + resourceHelper.gs(R.string.bolusdelivering, requestedBolusAmount), + percent.toInt() + ) + + val sleepSeconds = if (bolusCanceled) + BOLUS_RETRY_INTERVAL_MS + else + estimateBolusDeliverySeconds(remainingUnits) + Thread.sleep(sleepSeconds * 1000.toLong()) } else { // delivery is complete. If pod is Kaput, we are handling this in getPodStatus - // TODO: race with cancel!! return@defer Single.just(podStateManager.lastBolus!!.deliveredUnits()!!) } } Single.just(requestedBolusAmount) // will be updated later! } + private fun cancelBolus(): Completable { + return executeProgrammingCommand( + historyEntry = history.createRecord(commandType = OmnipodCommandType.CANCEL_BOLUS), + command = omnipodManager.stopBolus().ignoreElements() + ) + } + private fun estimateBolusDeliverySeconds(requestedBolusAmount: Double): Long { return ceil(requestedBolusAmount / 0.05).toLong() * 2 + 3 } @@ -446,18 +488,6 @@ class OmnipodDashPumpPlugin @Inject constructor( override fun stopBolusDelivering() { aapsLogger.info(LTag.PUMP, "stopBolusDelivering called") bolusCanceled = true - for (tries in 1..10) { - val ret = executeProgrammingCommand( - historyEntry = history.createRecord(OmnipodCommandType.CANCEL_BOLUS), - command = omnipodManager.stopBolus().ignoreElements() - ).subscribeOn(aapsSchedulers.io) // stopBolusDelivering is executed on the main thread - .toPumpEnactResult() - aapsLogger.info(LTag.PUMP, "stopBolusDelivering finished with result: $ret") - if (ret.success) { - return - } - Thread.sleep(500) - } } override fun setTempBasalAbsolute( @@ -499,6 +529,7 @@ class OmnipodDashPumpPlugin @Inject constructor( .map { pumpSyncTempBasal(absoluteRate, durationInMinutes.toLong(), tbrType) } .ignoreElements(), ).toPumpEnactResult() + // TODO: add details about TBR aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute: result=$ret") return ret } @@ -778,11 +809,12 @@ class OmnipodDashPumpPlugin @Inject constructor( { historyId -> podStateManager.createActiveCommand(historyId) }, command: Completable, post: Completable = Completable.complete(), + checkNoActiveCommand: Boolean = true ): Completable { return Completable.concat( listOf( pre, - podStateManager.observeNoActiveCommand().ignoreElements(), + podStateManager.observeNoActiveCommand(checkNoActiveCommand).ignoreElements(), historyEntry .flatMap { activeCommandEntry(it) } .ignoreElement(), @@ -915,7 +947,7 @@ class OmnipodDashPumpPlugin @Inject constructor( aapsLogger.warn( LTag.PUMP, "Will not sync confirmed command of type: $historyEntry and " + - "succes: ${confirmation.success}" + "success: ${confirmation.success}" ) } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt index af459f1ba0..a01a3b985b 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/comm/OmnipodDashBleManagerImpl.kt @@ -97,7 +97,7 @@ class OmnipodDashBleManagerImpl @Inject constructor( } override fun getStatus(): ConnectionState { - return connection?.let { getStatus() } + return connection?.let { it.connectionState() } ?: NotConnected } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/DeliveryStatus.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/DeliveryStatus.kt index db2a6b3710..106a3f3ffe 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/DeliveryStatus.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/definition/DeliveryStatus.kt @@ -11,4 +11,16 @@ enum class DeliveryStatus(override val value: Byte) : HasValue { BOLUS_AND_BASAL_ACTIVE(0x05.toByte()), BOLUS_AND_TEMP_BASAL_ACTIVE(0x06.toByte()), UNKNOWN(0xff.toByte()); + + fun bolusDeliveringActive(): Boolean { + return value in arrayOf(BOLUS_AND_BASAL_ACTIVE.value, BOLUS_AND_TEMP_BASAL_ACTIVE.value) + } + + fun tempBasalActive(): Boolean { + return value in arrayOf(BOLUS_AND_TEMP_BASAL_ACTIVE.value, TEMP_BASAL_ACTIVE.value) + } + + fun suspended(): Boolean { + return value == SUSPENDED.value + } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt index 5fccff8b4f..420a57c017 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManager.kt @@ -84,7 +84,7 @@ interface OmnipodDashPodStateManager { requestedBolus: Double? = null ): Single fun updateActiveCommand(): Maybe - fun observeNoActiveCommand(): Observable + fun observeNoActiveCommand(b: Boolean): Observable fun getCommandConfirmationFromState(): CommandConfirmationFromState fun createLastBolus(requestedUnits: Double, historyId: String, bolusType: DetailedBolusInfo.BolusType) @@ -108,13 +108,13 @@ interface OmnipodDashPodStateManager { val startTime: Long, val requestedUnits: Double, var bolusUnitsRemaining: Double, - var complete: Boolean, + var deliveryComplete: Boolean, val historyId: String, val bolusType: DetailedBolusInfo.BolusType ) { fun deliveredUnits(): Double? { - return if (complete) { + return if (deliveryComplete) { requestedUnits - bolusUnitsRemaining } else { null @@ -122,7 +122,7 @@ interface OmnipodDashPodStateManager { } fun markComplete(): Double { - this.complete = true + this.deliveryComplete = true return requestedUnits - bolusUnitsRemaining } } diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt index f4376d443e..46d0a3998e 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/driver/pod/state/OmnipodDashPodStateManagerImpl.kt @@ -208,7 +208,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( startTime = System.currentTimeMillis(), requestedUnits = requestedUnits, bolusUnitsRemaining = requestedUnits, - complete = false, // cancelled, delivered 100% or pod failure + deliveryComplete = false, // cancelled, delivered 100% or pod failure historyId = historyId, bolusType = bolusType ) @@ -219,7 +219,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( val lastBolus = podState.lastBolus lastBolus?.run { - this.complete = true + this.deliveryComplete = true } ?: logger.error(LTag.PUMP, "Trying to mark null bolus as complete") @@ -231,7 +231,7 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( val remainingUnits = bolusPulsesRemaining.toDouble() * 0.05 this.bolusUnitsRemaining = remainingUnits if (remainingUnits == 0.0) { - this.complete = true + this.deliveryComplete = true } } } @@ -269,9 +269,9 @@ class OmnipodDashPodStateManagerImpl @Inject constructor( } @Synchronized - override fun observeNoActiveCommand(): Observable { + override fun observeNoActiveCommand(check: Boolean): Observable { return Observable.defer { - if (activeCommand == null) { + if (activeCommand == null || !check) { Observable.empty() } else { logger.warn(LTag.PUMP, "Active command already existing: $activeCommand") diff --git a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt index 352fa9cf82..6b9bbbd508 100644 --- a/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt +++ b/omnipod-dash/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/dash/ui/OmnipodDashOverviewFragment.kt @@ -472,7 +472,7 @@ class OmnipodDashOverviewFragment : DaggerFragment() { resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(Duration(it.startTime, System.currentTimeMillis())) ) - if (!it.complete) { + if (!it.deliveryComplete) { textColor = Color.YELLOW } podInfoBinding.lastBolus.text = text