From b1abc55defeadf9701a1c7c68683d546b06f10d4 Mon Sep 17 00:00:00 2001 From: Bart Sopers Date: Wed, 2 Sep 2020 20:48:56 +0200 Subject: [PATCH] - Improve Pod state accuracy for basal profile & TBR - Add certainty info about last bolus and temp basal to Pod state and display in OmnipodFragment - Improve user notifications of delivery errors# Please enter the commit message for your changes. Lines starting --- .../action/InsertCannulaAction.java | 1 + .../action/SetBasalScheduleAction.java | 4 +- .../action/SetTempBasalAction.java | 6 +- .../driver/manager/OmnipodManager.java | 78 +++++----- .../driver/manager/PodStateManager.java | 66 ++++++++- .../omnipod/manager/AapsOmnipodManager.java | 138 ++++++++++-------- .../pump/omnipod/ui/OmnipodFragment.kt | 76 ++++++---- omnipod/src/main/res/values/strings.xml | 15 +- 8 files changed, 245 insertions(+), 139 deletions(-) diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java index cae261d7ed..a96181a6eb 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/InsertCannulaAction.java @@ -37,6 +37,7 @@ public class InsertCannulaAction implements OmnipodAction { } if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.BASAL_INITIALIZED)) { + podStateManager.setBasalSchedule(initialBasalSchedule); service.programInitialBasalSchedule(communicationService, podStateManager, initialBasalSchedule); } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java index c3471e3bdf..a64766c5d3 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetBasalScheduleAction.java @@ -46,8 +46,6 @@ public class SetBasalScheduleAction implements OmnipodAction { OmnipodMessage basalMessage = new OmnipodMessage(podStateManager.getAddress(), Arrays.asList(setBasal, extraCommand), podStateManager.getMessageNumber()); - StatusResponse statusResponse = communicationService.exchangeMessages(StatusResponse.class, podStateManager, basalMessage); - podStateManager.setBasalSchedule(basalSchedule); - return statusResponse; + return communicationService.exchangeMessages(StatusResponse.class, podStateManager, basalMessage); } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java index c11458bce6..cadb602f68 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/communication/action/SetTempBasalAction.java @@ -1,6 +1,5 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action; -import org.joda.time.DateTime; import org.joda.time.Duration; import java.util.Arrays; @@ -11,7 +10,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetInsulinScheduleCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.TempBasalExtraCommand; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; -import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActionInitializationException; import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager; import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager; @@ -45,8 +43,6 @@ public class SetTempBasalAction implements OmnipodAction { new TempBasalExtraCommand(rate, duration, acknowledgementBeep, completionBeep, Duration.ZERO)); OmnipodMessage message = new OmnipodMessage(podStateManager.getAddress(), messageBlocks, podStateManager.getMessageNumber()); - StatusResponse statusResponse = communicationService.exchangeMessages(StatusResponse.class, podStateManager, message); - podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration); - return statusResponse; + return communicationService.exchangeMessages(StatusResponse.class, podStateManager, message); } } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java index 857527eb70..d89b1ac6fe 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/OmnipodManager.java @@ -212,6 +212,7 @@ public class OmnipodManager { try { executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule, false, podStateManager.getScheduleOffset(), acknowledgementBeep))); + podStateManager.setBasalSchedule(schedule); } catch (OmnipodException ex) { if (ex.isCertainFailure()) { if (!wasSuspended) { @@ -221,7 +222,9 @@ public class OmnipodManager { } // verifyDeliveryStatus will throw an exception if verification fails - if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { + if (verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { + podStateManager.setBasalSchedule(schedule); + } else { if (!wasSuspended) { throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but setting the new basal schedule did not", ex); } @@ -262,6 +265,7 @@ public class OmnipodManager { try { executeAndVerify(() -> communicationService.executeAction(new SetTempBasalAction( podStateManager, rate, duration, acknowledgementBeep, completionBeep))); + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, true); } catch (OmnipodException ex) { if (ex.isCertainFailure()) { if (cancelCurrentTbr) { @@ -271,13 +275,25 @@ public class OmnipodManager { } // verifyDeliveryStatus will throw an exception if verification fails - if (!verifyDeliveryStatus(DeliveryStatus.TEMP_BASAL_RUNNING, ex)) { - if (cancelCurrentTbr) { - throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex); - } + try { + if (verifyDeliveryStatus(DeliveryStatus.TEMP_BASAL_RUNNING, ex)) { + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, true); + } else { + if (cancelCurrentTbr) { + throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex); + } - ex.setCertainFailure(true); - throw ex; + ex.setCertainFailure(true); + throw ex; + } + } catch (OmnipodException ex2) { + if (!ex2.isCertainFailure()) { + // We're not sure that setting the new TBR failed, so we assume that it succeeded + // If it didn't, PodStateManager.updateFromResponse() will fix the state + // upon receiving the next StatusResponse + podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, false); + } + throw ex2; } } } finally { @@ -331,15 +347,16 @@ public class OmnipodManager { commandDeliveryStatus = CommandDeliveryStatus.UNCERTAIN_FAILURE; } - Duration bolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); - Duration estimatedRemainingBolusDuration = bolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); + DateTime estimatedBolusStartDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); + Duration estimatedBolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); + Duration estimatedRemainingBolusDuration = estimatedBolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); - DateTime startDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); - podStateManager.setLastBolus(startDate, units, estimatedRemainingBolusDuration); + podStateManager.setLastBolus(estimatedBolusStartDate, units, estimatedBolusDuration, commandDeliveryStatus == CommandDeliveryStatus.SUCCESS); CompositeDisposable disposables = new CompositeDisposable(); if (progressIndicationConsumer != null) { + int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10)); long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports; @@ -355,7 +372,7 @@ public class OmnipodManager { SingleSubject bolusCompletionSubject = SingleSubject.create(); synchronized (bolusDataMutex) { - activeBolusData = new ActiveBolusData(units, startDate, bolusCompletionSubject, disposables); + activeBolusData = new ActiveBolusData(units, estimatedBolusStartDate, commandDeliveryStatus, bolusCompletionSubject, disposables); } // Return successful command execution AFTER storing activeBolusData @@ -380,7 +397,7 @@ public class OmnipodManager { break; } } catch (PodFaultException ex) { - // Substract units not delivered in case of a Pod failure + // Subtract units not delivered in case of a Pod failure bolusNotDelivered = ex.getFaultEvent().getBolusNotDelivered(); aapsLogger.debug(LTag.PUMPCOMM, "Caught PodFaultException in bolus completion verification", ex); @@ -428,7 +445,7 @@ public class OmnipodManager { private void discardActiveBolusData(double bolusNotDelivered) { synchronized (bolusDataMutex) { double unitsDelivered = activeBolusData.getUnits() - bolusNotDelivered; - podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered, new Duration(activeBolusData.getStartDate(), DateTime.now())); + podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered, new Duration(activeBolusData.getStartDate(), DateTime.now()), activeBolusData.getCommandDeliveryStatus() == CommandDeliveryStatus.SUCCESS); activeBolusData.getDisposables().dispose(); activeBolusData.getBolusCompletionSubject().onSuccess(new BolusDeliveryResult(unitsDelivered)); activeBolusData = null; @@ -465,8 +482,6 @@ public class OmnipodManager { logStartingCommandExecution("setTime [acknowledgementBeeps=" + acknowledgementBeeps + "]"); try { - suspendDelivery(acknowledgementBeeps); - DateTimeZone oldTimeZone = podStateManager.getTimeZone(); try { @@ -476,20 +491,8 @@ public class OmnipodManager { setBasalSchedule(podStateManager.getBasalSchedule(), acknowledgementBeeps); } catch (OmnipodException ex) { - if (ex.isCertainFailure()) { - podStateManager.setTimeZone(oldTimeZone); - throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but resuming did not", ex); - } - - try { - // verifyDeliveryStatus will throw an exception if verification fails - if (!verifyDeliveryStatus(DeliveryStatus.NORMAL, ex)) { - throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but resuming did not", ex); - } - } catch (Exception ex2) { - podStateManager.setTimeZone(oldTimeZone); - throw ex2; - } + podStateManager.setTimeZone(oldTimeZone); + throw ex; } } finally { logCommandExecutionFinished("setTime"); @@ -620,13 +623,14 @@ public class OmnipodManager { */ private boolean verifyDeliveryStatus(DeliveryStatus expectedStatus, Throwable verificationCause) { aapsLogger.debug(LTag.PUMPCOMM, "Attempting to verify delivery status (expected={})", expectedStatus); - for (int i = 0; 2 > i; i++) { + for (int i = 0; 3 > i; i++) { try { StatusResponse podStatus = getPodStatus(); aapsLogger.debug(LTag.PUMPCOMM, "Resolved delivery status (expected={}, actual={})", expectedStatus, podStatus.getDeliveryStatus()); return podStatus.getDeliveryStatus().equals(expectedStatus); - } catch (Exception ignored) { - // ignore and try to continue + } catch (Exception ex) { + aapsLogger.debug(LTag.PUMPCOMM, "Ignoring exception thrown in getPodStatus() during attempt to verify delivery status: {}: {}", + ex.getClass().getSimpleName(), ex.getMessage()); } } aapsLogger.warn(LTag.PUMPCOMM, "Failed to verify delivery status"); @@ -694,12 +698,14 @@ public class OmnipodManager { private static class ActiveBolusData { private final double units; private final DateTime startDate; + private final CommandDeliveryStatus commandDeliveryStatus; private final SingleSubject bolusCompletionSubject; private final CompositeDisposable disposables; - private ActiveBolusData(double units, DateTime startDate, SingleSubject bolusCompletionSubject, CompositeDisposable disposables) { + private ActiveBolusData(double units, DateTime startDate, CommandDeliveryStatus commandDeliveryStatus, SingleSubject bolusCompletionSubject, CompositeDisposable disposables) { this.units = units; this.startDate = startDate; + this.commandDeliveryStatus = commandDeliveryStatus; this.bolusCompletionSubject = bolusCompletionSubject; this.disposables = disposables; } @@ -712,6 +718,10 @@ public class OmnipodManager { return startDate; } + CommandDeliveryStatus getCommandDeliveryStatus() { + return commandDeliveryStatus; + } + CompositeDisposable getDisposables() { return disposables; } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java index 453421e7d5..5a80c5c231 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/manager/PodStateManager.java @@ -357,14 +357,24 @@ public abstract class PodStateManager { return getSafe(() -> podState.getLastBolusDuration()); } - public final void setLastBolus(DateTime startTime, double amount, Duration duration) { + public final boolean isLastBolusCertain() { + Boolean certain = getSafe(() -> podState.isLastBolusCertain()); + return certain == null || certain; + } + + public final void setLastBolus(DateTime startTime, double amount, Duration duration, boolean certain) { setAndStore(() -> { podState.setLastBolusStartTime(startTime); podState.setLastBolusAmount(amount); podState.setLastBolusDuration(duration); + podState.setLastBolusCertain(certain); }); } + public final boolean hasLastBolus() { + return getLastBolusAmount() != null && getLastBolusDuration() != null && getLastBolusStartTime() != null; + } + public final DateTime getTempBasalStartTime() { return getSafe(() -> podState.getTempBasalStartTime()); } @@ -377,11 +387,16 @@ public abstract class PodStateManager { return getSafe(() -> podState.getTempBasalDuration()); } - public final void setTempBasal(DateTime startTime, Double amount, Duration duration) { - setTempBasal(startTime, amount, duration, true); + public final boolean isTempBasalCertain() { + Boolean certain = getSafe(() -> podState.isTempBasalCertain()); + return certain == null || certain; } - public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean store) { + public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean certain) { + setTempBasal(startTime, amount, duration, certain, true); + } + + public final void setTempBasal(DateTime startTime, Double amount, Duration duration, Boolean certain, boolean store) { DateTime currentStartTime = getTempBasalStartTime(); Double currentAmount = getTempBasalAmount(); Duration currentDuration = getTempBasalDuration(); @@ -390,6 +405,7 @@ public abstract class PodStateManager { podState.setTempBasalStartTime(startTime); podState.setTempBasalAmount(amount); podState.setTempBasalDuration(duration); + podState.setTempBasalCertain(certain); }; if (store) { @@ -401,10 +417,26 @@ public abstract class PodStateManager { } } + /** + * @return true when a Temp Basal is stored in the Pod Stated + * Please note that this could also be an expired Temp Basal. For an indication on whether or not + * a temp basal is actually running, use {@link #isTempBasalRunning() isTempBasalRunning()} + */ public final boolean hasTempBasal() { return getTempBasalAmount() != null && getTempBasalDuration() != null && getTempBasalStartTime() != null; } + /** + * @return true when a Temp Basal is stored in the Pod Stated and this temp basal is currently running (based on start time and duration) + */ + public final boolean isTempBasalRunning() { + if (hasTempBasal()) { + DateTime tempBasalEndTime = getTempBasalStartTime().plus(getTempBasalDuration()); + return DateTime.now().isBefore(tempBasalEndTime); + } + return false; + } + public final DeliveryStatus getLastDeliveryStatus() { return getSafe(() -> podState.getLastDeliveryStatus()); } @@ -430,9 +462,13 @@ public abstract class PodStateManager { podState.setReservoirLevel(statusResponse.getReservoirLevel()); podState.setTotalTicksDelivered(statusResponse.getTicksDelivered()); podState.setPodProgressStatus(statusResponse.getPodProgressStatus()); - if (!statusResponse.getDeliveryStatus().isTbrRunning()) { + if (statusResponse.getDeliveryStatus().isTbrRunning()) { + if (!isTempBasalCertain() && isTempBasalRunning()) { + podState.setTempBasalCertain(true); + } + } else { // Triggers {@link #onTbrChanged() onTbrChanged()} when appropriate - setTempBasal(null, null, null, false); + setTempBasal(null, null, null, true, false); } podState.setLastUpdatedFromResponse(DateTime.now()); }); @@ -538,9 +574,11 @@ public abstract class PodStateManager { private DateTime lastBolusStartTime; private Double lastBolusAmount; private Duration lastBolusDuration; + private Boolean lastBolusCertain; private Double tempBasalAmount; private DateTime tempBasalStartTime; private Duration tempBasalDuration; + private Boolean tempBasalCertain; private final Map configuredAlerts = new HashMap<>(); private PodState(int address) { @@ -751,6 +789,14 @@ public abstract class PodStateManager { this.lastBolusDuration = lastBolusDuration; } + Boolean isLastBolusCertain() { + return lastBolusCertain; + } + + void setLastBolusCertain(Boolean certain) { + this.lastBolusCertain = certain; + } + Double getTempBasalAmount() { return tempBasalAmount; } @@ -775,6 +821,14 @@ public abstract class PodStateManager { this.tempBasalDuration = tempBasalDuration; } + Boolean isTempBasalCertain() { + return tempBasalCertain; + } + + void setTempBasalCertain(Boolean certain) { + this.tempBasalCertain = certain; + } + Map getConfiguredAlerts() { return configuredAlerts; } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java index b5d7277b7b..dd3d20aac5 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/manager/AapsOmnipodManager.java @@ -43,6 +43,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.definition.PodInitReceive import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog; import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse; +import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants; import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType; @@ -152,17 +153,15 @@ public class AapsOmnipodManager { return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name())); } - long time = System.currentTimeMillis(); - try { Disposable disposable = delegate.pairAndPrime().subscribe(res -> // - handleSetupActionResult(podInitActionType, podInitReceiver, res, time, null)); + handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), null)); return new PumpEnactResult(injector).success(true).enacted(true); } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment); - addFailureToHistory(time, PodHistoryEntryType.PAIR_AND_PRIME, comment); + addFailureToHistory(System.currentTimeMillis(), PodHistoryEntryType.PAIR_AND_PRIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } @@ -172,8 +171,6 @@ public class AapsOmnipodManager { return new PumpEnactResult(injector).success(false).enacted(false).comment(getStringResource(R.string.omnipod_error_illegal_init_action_type, podInitActionType.name())); } - long time = System.currentTimeMillis(); - try { BasalSchedule basalSchedule; try { @@ -182,7 +179,7 @@ public class AapsOmnipodManager { throw new CommandInitializationException("Basal profile mapping failed", ex); } Disposable disposable = delegate.insertCannula(basalSchedule).subscribe(res -> // - handleSetupActionResult(podInitActionType, podInitReceiver, res, time, profile)); + handleSetupActionResult(podInitActionType, podInitReceiver, res, System.currentTimeMillis(), profile)); rxBus.send(new EventDismissNotification(Notification.OMNIPOD_POD_NOT_ATTACHED)); @@ -192,36 +189,34 @@ public class AapsOmnipodManager { } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(podInitActionType, false, comment); - addFailureToHistory(time, PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, comment); + addFailureToHistory(PodHistoryEntryType.FILL_CANNULA_SET_BASAL_PROFILE, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } public PumpEnactResult getPodStatus() { - long time = System.currentTimeMillis(); try { StatusResponse statusResponse = delegate.getPodStatus(); - addSuccessToHistory(time, PodHistoryEntryType.GET_POD_STATUS, statusResponse); + addSuccessToHistory(PodHistoryEntryType.GET_POD_STATUS, statusResponse); return new PumpEnactResult(injector).success(true).enacted(false); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.GET_POD_STATUS, comment); + addFailureToHistory(PodHistoryEntryType.GET_POD_STATUS, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } } public PumpEnactResult deactivatePod(PodInitReceiver podInitReceiver) { - long time = System.currentTimeMillis(); try { delegate.deactivatePod(); } catch (Exception ex) { String comment = handleAndTranslateException(ex); podInitReceiver.returnInitTaskStatus(PodInitActionType.DEACTIVATE_POD_WIZARD_STEP, false, comment); - addFailureToHistory(time, PodHistoryEntryType.DEACTIVATE_POD, comment); + addFailureToHistory(PodHistoryEntryType.DEACTIVATE_POD, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - addSuccessToHistory(time, PodHistoryEntryType.DEACTIVATE_POD, null); + addSuccessToHistory(PodHistoryEntryType.DEACTIVATE_POD, null); createSuspendedFakeTbrIfNotExists(); @@ -231,7 +226,6 @@ public class AapsOmnipodManager { } public PumpEnactResult setBasalProfile(Profile profile) { - long time = System.currentTimeMillis(); PodHistoryEntryType historyEntryType = podStateManager.isSuspended() ? PodHistoryEntryType.RESUME_DELIVERY : PodHistoryEntryType.SET_BASAL_SCHEDULE; try { @@ -243,25 +237,24 @@ public class AapsOmnipodManager { } delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled()); - time = System.currentTimeMillis(); if (historyEntryType == PodHistoryEntryType.RESUME_DELIVERY) { cancelSuspendedFakeTbrIfExists(); } - addSuccessToHistory(time, historyEntryType, profile.getBasalValues()); + addSuccessToHistory(historyEntryType, profile.getBasalValues()); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { createSuspendedFakeTbrIfNotExists(); String comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, historyEntryType, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { String comment = getStringResource(R.string.omnipod_error_set_basal_failed_delivery_might_be_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, historyEntryType, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, historyEntryType, comment); + addFailureToHistory(historyEntryType, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } @@ -302,8 +295,11 @@ public class AapsOmnipodManager { if (OmnipodManager.CommandDeliveryStatus.UNCERTAIN_FAILURE.equals(bolusCommandResult.getCommandDeliveryStatus())) { // For safety reasons, we treat this as a bolus that has successfully been delivered, in order to prevent insulin overdose - - showErrorDialog(getStringResource(R.string.omnipod_bolus_failed_uncertain), R.raw.boluserror); + if (detailedBolusInfo.isSMB) { + showNotification(getStringResource(R.string.omnipod_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, R.raw.boluserror); + } else { + showErrorDialog(getStringResource(R.string.omnipod_bolus_failed_uncertain), R.raw.boluserror); + } } detailedBolusInfo.date = bolusStarted.getTime(); @@ -393,47 +389,56 @@ public class AapsOmnipodManager { public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) { boolean beepsEnabled = isTbrBeepsEnabled(); - long time = System.currentTimeMillis(); try { delegate.setTemporaryBasal(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), beepsEnabled, beepsEnabled); - time = System.currentTimeMillis(); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { String comment = getStringResource(R.string.omnipod_cancelled_old_tbr_failed_to_set_new); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + showNotification(comment, Notification.URGENT, null); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { - String comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled); + String comment; + if (ex.getExpectedStatus() == DeliveryStatus.TEMP_BASAL_RUNNING) { + // Happened after cancelling the old TBR, when attempting to set new TBR + + comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_cancelled_new_might_have_failed); + long pumpId = addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + + // Assume that setting the temp basal succeeded here, because in case it didn't succeed, + // The next StatusResponse that we receive will allow us to recover from the wrong state + // as we can see that the delivery status doesn't actually show that a TBR is running + // If we would assume that the TBR didn't succeed, we couldn't properly recover upon the next StatusResponse, + // as we could only see that the Pod is running a TBR, but we don't know the rate and duration as + // the Pod doesn't provide this information + addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair); + } else { + // Happened when attempting to cancel the old TBR + comment = getStringResource(R.string.omnipod_error_set_temp_basal_failed_old_tbr_might_be_cancelled); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + } + showNotification(comment, Notification.URGENT, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - long pumpId = addSuccessToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair); + long pumpId = addSuccessToHistory(PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair); - TemporaryBasal tempStart = new TemporaryBasal(injector) // - .date(time) // - .duration(tempBasalPair.getDurationMinutes()) // - .absolute(tempBasalPair.getInsulinRate()) // - .pumpId(pumpId) - .source(Source.PUMP); - - activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); + addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair); return new PumpEnactResult(injector).success(true).enacted(true); } public PumpEnactResult cancelTemporaryBasal() { - long time = System.currentTimeMillis(); try { delegate.cancelTemporaryBasal(isTbrBeepsEnabled()); - addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null); + addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, null); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, comment); + addFailureToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } @@ -441,30 +446,27 @@ public class AapsOmnipodManager { } public PumpEnactResult acknowledgeAlerts() { - long time = System.currentTimeMillis(); try { delegate.acknowledgeAlerts(); - addSuccessToHistory(time, PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null); + addSuccessToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, null); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.ACKNOWLEDGE_ALERTS, comment); + addFailureToHistory(PodHistoryEntryType.ACKNOWLEDGE_ALERTS, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } return new PumpEnactResult(injector).success(true).enacted(true); } public PumpEnactResult suspendDelivery() { - long time = System.currentTimeMillis(); - try { delegate.suspendDelivery(isBasalBeepsEnabled()); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SUSPEND_DELIVERY, comment); + addFailureToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } - addSuccessToHistory(time, PodHistoryEntryType.SUSPEND_DELIVERY, null); + addSuccessToHistory(PodHistoryEntryType.SUSPEND_DELIVERY, null); createSuspendedFakeTbrIfNotExists(); @@ -473,25 +475,23 @@ public class AapsOmnipodManager { // Updates the pods current time based on the device timezone and the pod's time zone public PumpEnactResult setTime() { - long time = System.currentTimeMillis(); try { delegate.setTime(isBasalBeepsEnabled()); - time = System.currentTimeMillis(); - addSuccessToHistory(time, PodHistoryEntryType.SET_TIME, null); + addSuccessToHistory(PodHistoryEntryType.SET_TIME, null); } catch (CommandFailedAfterChangingDeliveryStatusException ex) { createSuspendedFakeTbrIfNotExists(); String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (DeliveryStatusVerificationFailedException ex) { String comment = getStringResource(R.string.omnipod_error_set_time_failed_delivery_might_be_suspended); - showErrorDialog(comment, R.raw.boluserror); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + showNotification(comment, Notification.URGENT, R.raw.boluserror); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } catch (Exception ex) { String comment = handleAndTranslateException(ex); - addFailureToHistory(time, PodHistoryEntryType.SET_TIME, comment); + addFailureToHistory(PodHistoryEntryType.SET_TIME, comment); return new PumpEnactResult(injector).success(false).enacted(false).comment(comment); } @@ -587,13 +587,12 @@ public class AapsOmnipodManager { } public void reportCancelledTbr() { - long time = System.currentTimeMillis(); aapsLogger.debug(LTag.PUMP, "Reporting cancelled TBR to AAPS"); - long pumpId = addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null); + long pumpId = addSuccessToHistory(PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null); TemporaryBasal temporaryBasal = new TemporaryBasal(injector) // - .date(time) // + .date(System.currentTimeMillis()) // .duration(0) // .source(Source.PUMP) // .pumpId(pumpId); @@ -601,10 +600,29 @@ public class AapsOmnipodManager { activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal); } + private void addTempBasalTreatment(long time, long pumpId, TempBasalPair tempBasalPair) { + TemporaryBasal tempStart = new TemporaryBasal(injector) // + .date(time) // + .duration(tempBasalPair.getDurationMinutes()) // + .absolute(tempBasalPair.getInsulinRate()) // + .pumpId(pumpId) + .source(Source.PUMP); + + activePlugin.getActiveTreatments().addToHistoryTempBasal(tempStart); + } + + private long addSuccessToHistory(PodHistoryEntryType entryType, Object data) { + return addSuccessToHistory(System.currentTimeMillis(), entryType, data); + } + private long addSuccessToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { return addToHistory(requestTime, entryType, data, true); } + private long addFailureToHistory(PodHistoryEntryType entryType, Object data) { + return addFailureToHistory(System.currentTimeMillis(), entryType, data); + } + private long addFailureToHistory(long requestTime, PodHistoryEntryType entryType, Object data) { return addToHistory(requestTime, entryType, data, false); } diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt index feddf6fd64..1eccabe8d1 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/ui/OmnipodFragment.kt @@ -205,6 +205,8 @@ class OmnipodFragment : DaggerFragment() { private fun updateOmnipodStatus() { updateLastConnection() + updateLastBolus() + updateTempBasal() updatePodStatus() val errors = ArrayList(); @@ -228,8 +230,6 @@ class OmnipodFragment : DaggerFragment() { omnipod_base_basal_rate.text = PLACEHOLDER omnipod_total_delivered.text = PLACEHOLDER omnipod_reservoir.text = PLACEHOLDER - omnipod_temp_basal.text = PLACEHOLDER - omnipod_last_bolus.text = PLACEHOLDER omnipod_pod_active_alerts.text = PLACEHOLDER } else { omnipod_pod_address.text = podStateManager.address.toString() @@ -248,13 +248,6 @@ class OmnipodFragment : DaggerFragment() { errors.add(resourceHelper.gs(R.string.omnipod_pod_status_pod_fault_description, faultEventCode.value, faultEventCode.name)) } - // last bolus - omnipod_last_bolus.text = if (podStateManager.lastBolusStartTime != null && podStateManager.lastBolusAmount != null) { - resourceHelper.gs(R.string.omnipod_last_bolus, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime)) - } else { - PLACEHOLDER - } - val now = DateTime.now() // base basal rate @@ -264,22 +257,6 @@ class OmnipodFragment : DaggerFragment() { PLACEHOLDER } - // Temp basal - val lastTempBasalStartTime = podStateManager.tempBasalStartTime; - val lastTempBasalAmount = podStateManager.tempBasalAmount - val lastTempBasalDuration = podStateManager.tempBasalDuration; - if (lastTempBasalStartTime != null && lastTempBasalAmount != null && lastTempBasalDuration != null) { - val endTime = lastTempBasalStartTime.plus(lastTempBasalDuration); - val minutesRunning = Duration(lastTempBasalStartTime, now).standardMinutes - omnipod_temp_basal.text = if (endTime.isAfter(now)) { - resourceHelper.gs(R.string.omnipod_temp_basal, lastTempBasalAmount, dateUtil.timeString(lastTempBasalStartTime.millis), minutesRunning, lastTempBasalDuration.standardMinutes) - } else { - PLACEHOLDER - } - } else { - omnipod_temp_basal.text = PLACEHOLDER - } - // total delivered omnipod_total_delivered.text = if (podStateManager.isPodActivationCompleted && podStateManager.totalInsulinDelivered != null) { resourceHelper.gs(R.string.omnipod_total_delivered, podStateManager.totalInsulinDelivered - OmnipodConstants.POD_SETUP_UNITS); @@ -371,6 +348,55 @@ class OmnipodFragment : DaggerFragment() { omnipod_pod_status.setTextColor(podStatusColor) } + private fun updateLastBolus() { + if (podStateManager.isPodActivationCompleted && podStateManager.hasLastBolus()) { + var text = resourceHelper.gs(R.string.omnipod_last_bolus, omnipodPumpPlugin.model().determineCorrectBolusSize(podStateManager.lastBolusAmount), resourceHelper.gs(R.string.insulin_unit_shortname), readableDuration(podStateManager.lastBolusStartTime)) + val textColor: Int + + if (podStateManager.isLastBolusCertain) { + textColor = Color.WHITE + } else { + textColor = Color.RED + text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" + } + + omnipod_last_bolus.text = text; + omnipod_last_bolus.setTextColor(textColor) + + } else { + omnipod_last_bolus.text = PLACEHOLDER + omnipod_last_bolus.setTextColor(Color.WHITE) + } + } + + private fun updateTempBasal() { + if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) { + val now = DateTime.now() + + val startTime = podStateManager.tempBasalStartTime; + val amount = podStateManager.tempBasalAmount + val duration = podStateManager.tempBasalDuration; + + val minutesRunning = Duration(startTime, now).standardMinutes + + var text: String + val textColor: Int + text = resourceHelper.gs(R.string.omnipod_temp_basal, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes) + if (podStateManager.isTempBasalCertain) { + textColor = Color.WHITE + } else { + textColor = Color.RED + text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")" + } + + omnipod_temp_basal.text = text; + omnipod_temp_basal.setTextColor(textColor) + } else { + omnipod_temp_basal.text = PLACEHOLDER + omnipod_temp_basal.setTextColor(Color.WHITE) + } + } + private fun updateQueueStatus() { if (isQueueEmpty()) { omnipod_queue.visibility = View.GONE diff --git a/omnipod/src/main/res/values/strings.xml b/omnipod/src/main/res/values/strings.xml index 73100ee808..a1b825a75f 100644 --- a/omnipod/src/main/res/values/strings.xml +++ b/omnipod/src/main/res/values/strings.xml @@ -133,12 +133,14 @@ Shutdown is imminent Low reservoir Unknown alert - Setting basal profile might have failed. Delivery might be suspended! Please refresh Pod status and resume delivery from the Omnipod tab if needed. - Setting basal profile failed. Delivery is suspended! Please resume delivery from the Omnipod tab. - Setting temp basal might have failed. If a temp basal was already running, it might have been cancelled without AndroidAPS being aware! - Setting time might have failed. Delivery might be suspended! Please refresh Pod status and resume delivery from the Omnipod tab if needed. - Setting time failed. Delivery is suspended! Please resume delivery from the Omnipod tab. - Unable to verify whether the bolus succeeded. Please verify that your Pod is bolusing or cancel the bolus. + Setting basal profile might have failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed. + Setting basal profile failed. Delivery is suspended! Please manually resume delivery from the Omnipod tab. + Setting temp basal failed. If a temp basal was previously running, it might have been cancelled. Please manually refresh the Pod status from the Omnipod tab. + Setting temp might have basal failed. If a temp basal was previously running, it has been cancelled. Please manually refresh the Pod status from the Omnipod tab. + Setting time might have failed. Delivery might be suspended! Please manually refresh the Pod status from the Omnipod tab and resume delivery if needed. + Setting time failed. Delivery is suspended! Please manually resume delivery from the Omnipod tab. + Unable to verify whether the bolus succeeded. Please manually verify that your Pod is bolusing by listening to clicks. If you are sure that the bolus didn\'t succeed, you should manually delete the bolus entry from Treatments, even if you click \'Cancel bolus\' now! + Unable to verify whether SMB bolus (%1$.2f U) succeeded. If you are sure that the Bolus didn\'t succeed, you should manually delete the SMB entry from Treatments. RL stats Pulse log LOT @@ -165,6 +167,7 @@ Cancelled the old temporary basal, but failed to set new temporary basal Set fake temporary basal because the Pod is suspended Cancel fake temporary basal that was created because the Pod was suspended + uncertain %1$d minute