diff --git a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/OmnipodConstants.java b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/OmnipodConstants.java index b36dc765c7..b5b65f0f58 100644 --- a/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/OmnipodConstants.java +++ b/omnipod/src/main/java/info/nightscout/androidaps/plugins/pump/omnipod/driver/definition/OmnipodConstants.java @@ -21,10 +21,8 @@ public class OmnipodConstants { public static final Duration AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION = Duration.millis(1500); public static final Duration SERVICE_DURATION = Duration.standardHours(80); - public static final Duration EXPIRATION_ADVISORY_WINDOW = Duration.standardHours(9); public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1); public static final Duration NOMINAL_POD_LIFE = Duration.standardHours(72); - public static final double LOW_RESERVOIR_ALERT = 20.0; public static final double POD_PRIME_BOLUS_UNITS = 2.6; public static final double POD_CANNULA_INSERTION_BOLUS_UNITS = 0.5; 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 9da707a26b..fa6e08706a 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 @@ -126,9 +126,9 @@ public class OmnipodManager { logCommandExecutionFinished("pairAndPrime"); } - long delayInSeconds = calculateBolusDuration(OmnipodConstants.POD_PRIME_BOLUS_UNITS, OmnipodConstants.POD_PRIMING_DELIVERY_RATE).getStandardSeconds(); + long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_PRIME_BOLUS_UNITS, OmnipodConstants.POD_PRIMING_DELIVERY_RATE).getMillis(); - return Single.timer(delayInSeconds, TimeUnit.SECONDS) // + return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) // .map(o -> verifySetupAction(PodProgressStatus.PRIMING_COMPLETED)) // .observeOn(Schedulers.io()); } @@ -154,9 +154,9 @@ public class OmnipodManager { logCommandExecutionFinished("insertCannula"); } - long delayInSeconds = calculateBolusDuration(OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConstants.POD_CANNULA_INSERTION_DELIVERY_RATE).getStandardSeconds(); + long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConstants.POD_CANNULA_INSERTION_DELIVERY_RATE).getMillis(); - return Single.timer(delayInSeconds, TimeUnit.SECONDS) // + return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) // .map(o -> verifySetupAction(PodProgressStatus.ABOVE_FIFTY_UNITS)) // .observeOn(Schedulers.io()); } @@ -367,7 +367,7 @@ public class OmnipodManager { } DateTime estimatedBolusStartDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); - Duration estimatedBolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); + Duration estimatedBolusDuration = calculateEstimatedBolusDuration(estimatedBolusStartDate, units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); Duration estimatedRemainingBolusDuration = estimatedBolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION); podStateManager.setLastBolus(estimatedBolusStartDate, units, estimatedBolusDuration, commandDeliveryStatus == CommandDeliveryStatus.SUCCESS); @@ -376,7 +376,7 @@ public class OmnipodManager { if (progressIndicationConsumer != null) { - int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10)); + long numberOfProgressReports = Math.max(10, Math.min(100, estimatedRemainingBolusDuration.getStandardSeconds())); long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports; disposables.add(Flowable.intervalRange(0, numberOfProgressReports + 1, 0, progressReportInterval, TimeUnit.MILLISECONDS) // @@ -664,14 +664,27 @@ public class OmnipodManager { aapsLogger.debug(LTag.PUMPCOMM, "Command execution finished for action: " + action); } - private static Duration calculateBolusDuration(double units, double deliveryRate) { - // TODO take current (temp) basal into account - // Be aware that the Pod possibly doesn't have a Basal Schedule yet - return Duration.standardSeconds((long) Math.ceil(units / deliveryRate)); - } + private Duration calculateEstimatedBolusDuration(DateTime startTime, double units, double deliveryRateInUnitsPerSecond) { + if (!podStateManager.isPodActivationCompleted()) { + // No basal or temp basal is active yet + return Duration.standardSeconds((long) Math.ceil(units / deliveryRateInUnitsPerSecond)); + } - public static Duration calculateBolusDuration(double units) { - return calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE); + double pulseIntervalInSeconds = OmnipodConstants.POD_PULSE_SIZE / deliveryRateInUnitsPerSecond; + long numberOfPulses = Math.round(units / OmnipodConstants.POD_PULSE_SIZE); + double totalEstimatedDurationInSeconds = 0D; + + for (int i = 0; numberOfPulses > i; i++) { + DateTime estimatedTimeAtPulse = startTime.plusMillis((int) (totalEstimatedDurationInSeconds * 1000)); + double effectiveBasalRateAtPulse = podStateManager.getEffectiveBasalRateAt(estimatedTimeAtPulse); + double effectivePulsesPerHourAtPulse = effectiveBasalRateAtPulse / OmnipodConstants.POD_PULSE_SIZE; + double effectiveBasalPulsesPerSecondAtPulse = effectivePulsesPerHourAtPulse / 3600; + double effectiveBasalPulsesPerBolusPulse = pulseIntervalInSeconds * effectiveBasalPulsesPerSecondAtPulse; + + totalEstimatedDurationInSeconds += pulseIntervalInSeconds * (1 + effectiveBasalPulsesPerBolusPulse); + } + + return Duration.millis(Math.round(totalEstimatedDurationInSeconds * 1000)); } public static boolean isCertainFailure(Exception ex) { 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 4aed1057f6..bd228d2d0c 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 @@ -427,16 +427,50 @@ public abstract class PodStateManager { } /** - * @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) + * @return true when a Temp Basal is stored in the Pod State and this temp basal is currently running (based on start time and duration) */ public final boolean isTempBasalRunning() { + return isTempBasalRunningAt(DateTime.now()); + } + + /** + * @return true when a Temp Basal is stored in the Pod State and this temp basal is running at the given time (based on start time and duration) + */ + public final boolean isTempBasalRunningAt(DateTime time) { if (hasTempBasal()) { - DateTime tempBasalEndTime = getTempBasalStartTime().plus(getTempBasalDuration()); - return DateTime.now().isBefore(tempBasalEndTime); + DateTime tempBasalStartTime = getTempBasalStartTime(); + DateTime tempBasalEndTime = tempBasalStartTime.plus(getTempBasalDuration()); + return (time.isAfter(tempBasalStartTime) || time.isEqual(tempBasalStartTime)) && time.isBefore(tempBasalEndTime); } return false; } + /** + * @return the current effective basal rate (taking Pod suspension, TBR, and basal profile into account) + */ + public final double getEffectiveBasalRate() { + if (isSuspended()) { + return 0d; + } + return getEffectiveBasalRateAt(DateTime.now()); + } + + /** + * @return the effective basal rate at the given time (taking TBR, and basal profile into account) + * Suspension is not taken into account as we don't keep historic data of that + */ + public final double getEffectiveBasalRateAt(DateTime time) { + BasalSchedule basalSchedule = getSafe(() -> podState.getBasalSchedule()); + if (basalSchedule == null) { + return 0d; + } + if (isTempBasalRunningAt(time)) { + return getTempBasalAmount(); + } + Duration offset = new Duration(time.withTimeAtStartOfDay(), time); + return basalSchedule.rateAt(offset); + } + public final DeliveryStatus getLastDeliveryStatus() { return getSafe(() -> podState.getLastDeliveryStatus()); }