- Also show bolus progress dialog for uncertain boluses
- Improve bolus progress indication
This commit is contained in:
parent
ea5ff4cc3a
commit
dfb15e0f82
3 changed files with 88 additions and 58 deletions
|
@ -52,8 +52,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
import io.reactivex.disposables.CompositeDisposable;
|
import io.reactivex.disposables.CompositeDisposable;
|
||||||
import io.reactivex.subjects.SingleSubject;
|
import io.reactivex.subjects.SingleSubject;
|
||||||
|
|
||||||
import static info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION;
|
|
||||||
|
|
||||||
public class OmnipodManager {
|
public class OmnipodManager {
|
||||||
private static final int ACTION_VERIFICATION_TRIES = 3;
|
private static final int ACTION_VERIFICATION_TRIES = 3;
|
||||||
|
|
||||||
|
@ -161,22 +159,38 @@ public class OmnipodManager {
|
||||||
|
|
||||||
// Returns a SingleSubject that returns when the bolus has finished.
|
// Returns a SingleSubject that returns when the bolus has finished.
|
||||||
// When a bolus is cancelled, it will return after cancellation and report the estimated units delivered
|
// When a bolus is cancelled, it will return after cancellation and report the estimated units delivered
|
||||||
public synchronized SingleSubject<BolusResult> bolus(Double units, BolusProgressIndicationConsumer progressIndicationConsumer) {
|
// Only throws OmnipodException[certainFailure=false]
|
||||||
|
public synchronized BolusCommandResult bolus(Double units, BolusProgressIndicationConsumer progressIndicationConsumer) {
|
||||||
assertReadyForDelivery();
|
assertReadyForDelivery();
|
||||||
|
|
||||||
executeAndVerify(() -> communicationService.executeAction(new BolusAction(podState, units, true, true)));
|
CommandDeliveryStatus commandDeliveryStatus = CommandDeliveryStatus.SUCCESS;
|
||||||
|
|
||||||
DateTime startDate = DateTime.now().minus(AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
|
try {
|
||||||
|
executeAndVerify(() -> communicationService.executeAction(new BolusAction(podState, units, true, true)));
|
||||||
|
} catch (OmnipodException ex) {
|
||||||
|
if (ex.isCertainFailure()) {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch uncertain exceptions as we still want to report bolus progress indication
|
||||||
|
if (isLoggingEnabled()) {
|
||||||
|
LOG.error("Caught exception[certainFailure=false] in bolus", ex);
|
||||||
|
}
|
||||||
|
commandDeliveryStatus = CommandDeliveryStatus.UNCERTAIN_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime startDate = DateTime.now().minus(OmnipodConst.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
|
||||||
|
|
||||||
CompositeDisposable disposables = new CompositeDisposable();
|
CompositeDisposable disposables = new CompositeDisposable();
|
||||||
Duration bolusDuration = calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE);
|
Duration bolusDuration = calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE);
|
||||||
Duration estimatedRemainingBolusDuration = bolusDuration.minus(AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
|
Duration estimatedRemainingBolusDuration = bolusDuration.minus(OmnipodConst.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
|
||||||
|
|
||||||
if (progressIndicationConsumer != null) {
|
if (progressIndicationConsumer != null) {
|
||||||
int numberOfProgressReports = 20;
|
int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10));
|
||||||
long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports;
|
long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports;
|
||||||
|
|
||||||
disposables.add(Flowable.intervalRange(1, numberOfProgressReports, 0, progressReportInterval, TimeUnit.MILLISECONDS) //
|
disposables.add(Flowable.intervalRange(0, numberOfProgressReports + 1, 0, progressReportInterval, TimeUnit.MILLISECONDS) //
|
||||||
|
.observeOn(AndroidSchedulers.mainThread()) //
|
||||||
.subscribe(count -> {
|
.subscribe(count -> {
|
||||||
int percentage = (int) ((double) count / numberOfProgressReports * 100);
|
int percentage = (int) ((double) count / numberOfProgressReports * 100);
|
||||||
double estimatedUnitsDelivered = activeBolusData == null ? 0 : activeBolusData.estimateUnitsDelivered();
|
double estimatedUnitsDelivered = activeBolusData == null ? 0 : activeBolusData.estimateUnitsDelivered();
|
||||||
|
@ -184,10 +198,11 @@ public class OmnipodManager {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
SingleSubject<BolusResult> bolusCompletionSubject = SingleSubject.create();
|
SingleSubject<BolusDeliveryResult> bolusCompletionSubject = SingleSubject.create();
|
||||||
|
|
||||||
disposables.add(Completable.complete()
|
disposables.add(Completable.complete() //
|
||||||
.delay(estimatedRemainingBolusDuration.getStandardSeconds(), TimeUnit.SECONDS)
|
.delay(estimatedRemainingBolusDuration.getMillis() + 250, TimeUnit.MILLISECONDS) //
|
||||||
|
.observeOn(AndroidSchedulers.mainThread()) //
|
||||||
.doOnComplete(() -> {
|
.doOnComplete(() -> {
|
||||||
synchronized (bolusDataLock) {
|
synchronized (bolusDataLock) {
|
||||||
for (int i = 0; i < ACTION_VERIFICATION_TRIES; i++) {
|
for (int i = 0; i < ACTION_VERIFICATION_TRIES; i++) {
|
||||||
|
@ -207,7 +222,7 @@ public class OmnipodManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeBolusData != null) {
|
if (activeBolusData != null) {
|
||||||
activeBolusData.bolusCompletionSubject.onSuccess(new BolusResult(units));
|
activeBolusData.bolusCompletionSubject.onSuccess(new BolusDeliveryResult(units));
|
||||||
activeBolusData = null;
|
activeBolusData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,7 +233,7 @@ public class OmnipodManager {
|
||||||
activeBolusData = new ActiveBolusData(units, startDate, bolusCompletionSubject, disposables);
|
activeBolusData = new ActiveBolusData(units, startDate, bolusCompletionSubject, disposables);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bolusCompletionSubject;
|
return new BolusCommandResult(commandDeliveryStatus, bolusCompletionSubject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void cancelBolus() {
|
public synchronized void cancelBolus() {
|
||||||
|
@ -232,7 +247,7 @@ public class OmnipodManager {
|
||||||
executeAndVerify(() -> communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.BOLUS, true)));
|
executeAndVerify(() -> communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.BOLUS, true)));
|
||||||
|
|
||||||
activeBolusData.getDisposables().dispose();
|
activeBolusData.getDisposables().dispose();
|
||||||
activeBolusData.getBolusCompletionSubject().onSuccess(new BolusResult(activeBolusData.estimateUnitsDelivered()));
|
activeBolusData.getBolusCompletionSubject().onSuccess(new BolusDeliveryResult(activeBolusData.estimateUnitsDelivered()));
|
||||||
activeBolusData = null;
|
activeBolusData = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,7 +323,7 @@ public class OmnipodManager {
|
||||||
if (isCertainFailure(ex)) {
|
if (isCertainFailure(ex)) {
|
||||||
throw ex;
|
throw ex;
|
||||||
} else {
|
} else {
|
||||||
CommandVerificationResult verificationResult = verifyCommand();
|
CommandDeliveryStatus verificationResult = verifyCommand();
|
||||||
switch (verificationResult) {
|
switch (verificationResult) {
|
||||||
case CERTAIN_FAILURE:
|
case CERTAIN_FAILURE:
|
||||||
if (ex instanceof OmnipodException) {
|
if (ex instanceof OmnipodException) {
|
||||||
|
@ -335,12 +350,12 @@ public class OmnipodManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private SetupActionResult verifySetupAction(StatusResponseHandler setupActionResponseHandler, SetupProgress expectedSetupProgress) {
|
private SetupActionResult verifySetupAction(StatusResponseConsumer setupActionResponseHandler, SetupProgress expectedSetupProgress) {
|
||||||
SetupActionResult result = null;
|
SetupActionResult result = null;
|
||||||
for (int i = 0; ACTION_VERIFICATION_TRIES > i; i++) {
|
for (int i = 0; ACTION_VERIFICATION_TRIES > i; i++) {
|
||||||
try {
|
try {
|
||||||
StatusResponse delayedStatusResponse = communicationService.executeAction(new GetStatusAction(podState));
|
StatusResponse delayedStatusResponse = communicationService.executeAction(new GetStatusAction(podState));
|
||||||
setupActionResponseHandler.handle(delayedStatusResponse);
|
setupActionResponseHandler.accept(delayedStatusResponse);
|
||||||
|
|
||||||
if (podState.getSetupProgress().equals(expectedSetupProgress)) {
|
if (podState.getSetupProgress().equals(expectedSetupProgress)) {
|
||||||
result = new SetupActionResult(SetupActionResult.ResultType.SUCCESS);
|
result = new SetupActionResult(SetupActionResult.ResultType.SUCCESS);
|
||||||
|
@ -359,7 +374,7 @@ public class OmnipodManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only works for commands which contain nonce resyncable message blocks
|
// Only works for commands which contain nonce resyncable message blocks
|
||||||
private CommandVerificationResult verifyCommand() {
|
private CommandDeliveryStatus verifyCommand() {
|
||||||
if (isLoggingEnabled()) {
|
if (isLoggingEnabled()) {
|
||||||
LOG.warn("Verifying command by using cancel none command to verify nonce");
|
LOG.warn("Verifying command by using cancel none command to verify nonce");
|
||||||
}
|
}
|
||||||
|
@ -370,12 +385,12 @@ public class OmnipodManager {
|
||||||
if (isLoggingEnabled()) {
|
if (isLoggingEnabled()) {
|
||||||
LOG.info("Command resolved to FAILURE (CERTAIN_FAILURE)");
|
LOG.info("Command resolved to FAILURE (CERTAIN_FAILURE)");
|
||||||
}
|
}
|
||||||
return CommandVerificationResult.CERTAIN_FAILURE;
|
return CommandDeliveryStatus.CERTAIN_FAILURE;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
if (isLoggingEnabled()) {
|
if (isLoggingEnabled()) {
|
||||||
LOG.error("Command unresolved (UNCERTAIN_FAILURE)");
|
LOG.error("Command unresolved (UNCERTAIN_FAILURE)");
|
||||||
}
|
}
|
||||||
return CommandVerificationResult.UNCERTAIN_FAILURE;
|
return CommandDeliveryStatus.UNCERTAIN_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isLoggingEnabled()) {
|
if (isLoggingEnabled()) {
|
||||||
|
@ -383,7 +398,7 @@ public class OmnipodManager {
|
||||||
LOG.info("Command status resolved to SUCCESS");
|
LOG.info("Command status resolved to SUCCESS");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CommandVerificationResult.SUCCESS;
|
return CommandDeliveryStatus.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isLoggingEnabled() {
|
private boolean isLoggingEnabled() {
|
||||||
|
@ -402,10 +417,28 @@ public class OmnipodManager {
|
||||||
return ex instanceof OmnipodException && ((OmnipodException) ex).isCertainFailure();
|
return ex instanceof OmnipodException && ((OmnipodException) ex).isCertainFailure();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BolusResult {
|
public static class BolusCommandResult {
|
||||||
|
private final CommandDeliveryStatus commandDeliveryStatus;
|
||||||
|
private final SingleSubject<BolusDeliveryResult> deliveryResultSubject;
|
||||||
|
|
||||||
|
public BolusCommandResult(CommandDeliveryStatus commandDeliveryStatus, SingleSubject<BolusDeliveryResult> deliveryResultSubject) {
|
||||||
|
this.commandDeliveryStatus = commandDeliveryStatus;
|
||||||
|
this.deliveryResultSubject = deliveryResultSubject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandDeliveryStatus getCommandDeliveryStatus() {
|
||||||
|
return commandDeliveryStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SingleSubject<BolusDeliveryResult> getDeliveryResultSubject() {
|
||||||
|
return deliveryResultSubject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BolusDeliveryResult {
|
||||||
private final double unitsDelivered;
|
private final double unitsDelivered;
|
||||||
|
|
||||||
public BolusResult(double unitsDelivered) {
|
public BolusDeliveryResult(double unitsDelivered) {
|
||||||
this.unitsDelivered = unitsDelivered;
|
this.unitsDelivered = unitsDelivered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +447,7 @@ public class OmnipodManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum CommandVerificationResult {
|
public enum CommandDeliveryStatus {
|
||||||
SUCCESS,
|
SUCCESS,
|
||||||
CERTAIN_FAILURE,
|
CERTAIN_FAILURE,
|
||||||
UNCERTAIN_FAILURE
|
UNCERTAIN_FAILURE
|
||||||
|
@ -422,17 +455,17 @@ public class OmnipodManager {
|
||||||
|
|
||||||
// TODO replace with Consumer when our min API level >= 24
|
// TODO replace with Consumer when our min API level >= 24
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
private interface StatusResponseHandler {
|
private interface StatusResponseConsumer {
|
||||||
void handle(StatusResponse statusResponse);
|
void accept(StatusResponse statusResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ActiveBolusData {
|
private static class ActiveBolusData {
|
||||||
private final double units;
|
private final double units;
|
||||||
private volatile DateTime startDate;
|
private volatile DateTime startDate;
|
||||||
private volatile SingleSubject<BolusResult> bolusCompletionSubject;
|
private volatile SingleSubject<BolusDeliveryResult> bolusCompletionSubject;
|
||||||
private volatile CompositeDisposable disposables;
|
private volatile CompositeDisposable disposables;
|
||||||
|
|
||||||
private ActiveBolusData(double units, DateTime startDate, SingleSubject<BolusResult> bolusCompletionSubject, CompositeDisposable disposables) {
|
private ActiveBolusData(double units, DateTime startDate, SingleSubject<BolusDeliveryResult> bolusCompletionSubject, CompositeDisposable disposables) {
|
||||||
this.units = units;
|
this.units = units;
|
||||||
this.startDate = startDate;
|
this.startDate = startDate;
|
||||||
this.bolusCompletionSubject = bolusCompletionSubject;
|
this.bolusCompletionSubject = bolusCompletionSubject;
|
||||||
|
@ -451,11 +484,11 @@ public class OmnipodManager {
|
||||||
return disposables;
|
return disposables;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleSubject<BolusResult> getBolusCompletionSubject() {
|
public SingleSubject<BolusDeliveryResult> getBolusCompletionSubject() {
|
||||||
return bolusCompletionSubject;
|
return bolusCompletionSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBolusCompletionSubject(SingleSubject<BolusResult> bolusCompletionSubject) {
|
public void setBolusCompletionSubject(SingleSubject<BolusDeliveryResult> bolusCompletionSubject) {
|
||||||
this.bolusCompletionSubject = bolusCompletionSubject;
|
this.bolusCompletionSubject = bolusCompletionSubject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,6 @@ import info.nightscout.androidaps.plugins.pump.omnipod.exception.OmnipodExceptio
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.exception.PodFaultException;
|
import info.nightscout.androidaps.plugins.pump.omnipod.exception.PodFaultException;
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.exception.PodReturnedErrorResponseException;
|
import info.nightscout.androidaps.plugins.pump.omnipod.exception.PodReturnedErrorResponseException;
|
||||||
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
|
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodUtil;
|
||||||
import io.reactivex.Single;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
|
|
||||||
public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface {
|
public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface {
|
||||||
|
@ -148,44 +147,42 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
|
||||||
return new PumpEnactResult().success(true).enacted(true);
|
return new PumpEnactResult().success(true).enacted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add boolean isSmb so we can disable progress indication for SMB
|
||||||
@Override
|
@Override
|
||||||
public PumpEnactResult setBolus(Double units) {
|
public PumpEnactResult setBolus(Double units/*, boolean isSmb*/) {
|
||||||
|
OmnipodManager.BolusCommandResult bolusCommandResult;
|
||||||
try {
|
try {
|
||||||
Single<OmnipodManager.BolusResult> responseObserver = delegate.bolus(units,
|
bolusCommandResult = delegate.bolus(units, /* isSmb ? null : */
|
||||||
(estimatedUnitsDelivered, percentage) -> {
|
(estimatedUnitsDelivered, percentage) -> {
|
||||||
EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE;
|
EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE;
|
||||||
progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, units));
|
progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, units));
|
||||||
progressUpdateEvent.setPercent(percentage);
|
progressUpdateEvent.setPercent(percentage);
|
||||||
RxBus.INSTANCE.send(progressUpdateEvent);
|
RxBus.INSTANCE.send(progressUpdateEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
// At this point, we know that the bolus command has been succesfully sent
|
|
||||||
|
|
||||||
double unitsDelivered = units;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Wait for the bolus to finish
|
|
||||||
OmnipodManager.BolusResult bolusResult = responseObserver.blockingGet();
|
|
||||||
unitsDelivered = bolusResult.getUnitsDelivered();
|
|
||||||
} catch (Exception ex) {
|
|
||||||
if (loggingEnabled()) {
|
|
||||||
LOG.debug("Ignoring failed status response for bolus completion verification", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PumpEnactResult().success(true).enacted(true).bolusDelivered(unitsDelivered);
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Sending the command failed
|
|
||||||
String comment = handleAndTranslateException(ex);
|
String comment = handleAndTranslateException(ex);
|
||||||
if (OmnipodManager.isCertainFailure(ex)) {
|
return new PumpEnactResult().success(false).enacted(false).comment(comment);
|
||||||
return new PumpEnactResult().success(false).enacted(false).comment(comment);
|
}
|
||||||
} else {
|
|
||||||
// TODO notify user about uncertain failure
|
if (OmnipodManager.CommandDeliveryStatus.UNCERTAIN_FAILURE.equals(bolusCommandResult.getCommandDeliveryStatus()) /* && !isSmb */) {
|
||||||
// we don't know if the bolus failed, so for safety reasons, we choose to register the bolus as succesful.
|
// TODO notify user about uncertain failure ---> we're unsure whether or not the bolus has been delivered
|
||||||
// TODO also manually sleep until the bolus should have been finished here (after notifying the user)
|
// For safety reasons, we should treat this as a bolus that has been delivered, in order to prevent insulin overdose
|
||||||
return new PumpEnactResult().success(true).enacted(true).comment(comment).bolusDelivered(units);
|
}
|
||||||
|
|
||||||
|
double unitsDelivered = units;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wait for the bolus to finish
|
||||||
|
OmnipodManager.BolusDeliveryResult bolusDeliveryResult =
|
||||||
|
bolusCommandResult.getDeliveryResultSubject().blockingGet();
|
||||||
|
unitsDelivered = bolusDeliveryResult.getUnitsDelivered();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (loggingEnabled()) {
|
||||||
|
LOG.debug("Ignoring failed status response for bolus completion verification", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new PumpEnactResult().success(true).enacted(true).bolusDelivered(unitsDelivered);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class OmnipodConst {
|
||||||
public static final Duration MAX_TEMP_BASAL_DURATION = Duration.standardHours(12);
|
public static final Duration MAX_TEMP_BASAL_DURATION = Duration.standardHours(12);
|
||||||
public static final int DEFAULT_ADDRESS = 0xffffffff;
|
public static final int DEFAULT_ADDRESS = 0xffffffff;
|
||||||
|
|
||||||
public static final Duration AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION = Duration.standardSeconds(2);
|
public static final Duration AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION = Duration.millis(1500);
|
||||||
|
|
||||||
public static final Duration SERVICE_DURATION = Duration.standardHours(80);
|
public static final Duration SERVICE_DURATION = Duration.standardHours(80);
|
||||||
public static final Duration EXPIRATION_ADVISORY_WINDOW = Duration.standardHours(2);
|
public static final Duration EXPIRATION_ADVISORY_WINDOW = Duration.standardHours(2);
|
||||||
|
|
Loading…
Reference in a new issue