fix bolus cancelation

This commit is contained in:
Andrei Vereha 2021-06-19 13:13:43 +02:00
parent 59d0245980
commit 863fb19ff0
6 changed files with 95 additions and 51 deletions

View file

@ -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<Double> = 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}"
)
}
}

View file

@ -97,7 +97,7 @@ class OmnipodDashBleManagerImpl @Inject constructor(
}
override fun getStatus(): ConnectionState {
return connection?.let { getStatus() }
return connection?.let { it.connectionState() }
?: NotConnected
}

View file

@ -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
}
}

View file

@ -84,7 +84,7 @@ interface OmnipodDashPodStateManager {
requestedBolus: Double? = null
): Single<ActiveCommand>
fun updateActiveCommand(): Maybe<CommandConfirmed>
fun observeNoActiveCommand(): Observable<PodEvent>
fun observeNoActiveCommand(b: Boolean): Observable<PodEvent>
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
}
}

View file

@ -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<PodEvent> {
override fun observeNoActiveCommand(check: Boolean): Observable<PodEvent> {
return Observable.defer {
if (activeCommand == null) {
if (activeCommand == null || !check) {
Observable.empty()
} else {
logger.warn(LTag.PUMP, "Active command already existing: $activeCommand")

View file

@ -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