Merge branch 'omnipod_eros' of https://github.com/AAPS-Omnipod/AndroidAPS into omnipod_eros

This commit is contained in:
Andy Rozman 2019-12-08 11:57:24 +00:00
commit 73f1aaf819
16 changed files with 389 additions and 170 deletions

View file

@ -43,9 +43,6 @@ import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkConst;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ResetRileyLinkConfigurationTask;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor; import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.tasks.ServiceTaskExecutor;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.comm.AapsOmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUIComm;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUITask;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommandType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommunicationManagerInterface; import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCommunicationManagerInterface;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCustomActionType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodCustomActionType;
@ -54,9 +51,12 @@ import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodPumpPluginInt
import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodStatusRequest; import info.nightscout.androidaps.plugins.pump.omnipod.defs.OmnipodStatusRequest;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInitActionType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInitActionType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState; import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.comm.AapsOmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUIComm;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.ui.OmnipodUITask;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodPumpValuesChanged; import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodPumpValuesChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodRefreshButtonState; import info.nightscout.androidaps.plugins.pump.omnipod.events.EventOmnipodRefreshButtonState;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.OmnipodPumpStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.service.RileyLinkOmnipodService; import info.nightscout.androidaps.plugins.pump.omnipod.service.RileyLinkOmnipodService;
import info.nightscout.androidaps.plugins.pump.omnipod.util.LogReceiver; import info.nightscout.androidaps.plugins.pump.omnipod.util.LogReceiver;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst; import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst;
@ -314,7 +314,7 @@ public class OmnipodPumpPlugin extends PumpPluginAbstract implements OmnipodPump
@Override @Override
public boolean isSuspended() { public boolean isSuspended() {
return (pumpStatusLocal!=null && !pumpStatusLocal.podAvailable); return (pumpStatusLocal != null && !pumpStatusLocal.podAvailable);
} }
@Override @Override
@ -509,7 +509,7 @@ public class OmnipodPumpPlugin extends PumpPluginAbstract implements OmnipodPump
try { try {
OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.SetBolus, OmnipodUITask responseTask = omnipodUIComm.executeCommand(OmnipodCommandType.SetBolus,
detailedBolusInfo.insulin); detailedBolusInfo.insulin, detailedBolusInfo.isSMB);
PumpEnactResult result = responseTask.getResult(); PumpEnactResult result = responseTask.getResult();

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
// TODO replace with Consumer when our min API level >= 24
@FunctionalInterface
public interface BolusProgressIndicationConsumer {
void accept(double estimatedUnitsDelivered, int percentage);
}

View file

@ -155,17 +155,33 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
PacketType packetType = firstPacket ? PacketType.PDM : PacketType.CON; PacketType packetType = firstPacket ? PacketType.PDM : PacketType.CON;
OmnipodPacket packet = new OmnipodPacket(packetAddress, packetType, podState.getPacketNumber(), encodedMessage); OmnipodPacket packet = new OmnipodPacket(packetAddress, packetType, podState.getPacketNumber(), encodedMessage);
byte[] encodedMessageInPacket = packet.getEncodedMessage(); byte[] encodedMessageInPacket = packet.getEncodedMessage();
//getting the data remaining to be sent
// getting the data remaining to be sent
encodedMessage = ByteUtil.substring(encodedMessage, encodedMessageInPacket.length, encodedMessage.length - encodedMessageInPacket.length); encodedMessage = ByteUtil.substring(encodedMessage, encodedMessageInPacket.length, encodedMessage.length - encodedMessageInPacket.length);
firstPacket = false; firstPacket = false;
// If this is not the last packet, the message wasn't fully sent,
// so it's impossible for the pod to have received the message
boolean isCertainFailure = encodedMessage.length > 0;
try { try {
// We actually ignore previous (ack) responses if it was not last packet to send
response = exchangePackets(podState, packet); response = exchangePackets(podState, packet);
} catch (OmnipodException ex) {
throw ex;
} catch (Exception ex) { } catch (Exception ex) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex); OmnipodException newException;
if (ex instanceof OmnipodException) {
newException = (OmnipodException) ex;
} else {
newException = new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
}
if (isLoggingEnabled()) {
LOG.debug("Caught exception in transportMessages. Setting certainFailure to {} because encodedMessage.length={}", isCertainFailure, encodedMessage.length);
}
newException.setCertainFailure(isCertainFailure);
throw newException;
} }
//We actually ignore (ack) responses if it is not last packet to send
} }
if (response.getPacketType() == PacketType.ACK) { if (response.getPacketType() == PacketType.ACK) {
@ -266,7 +282,7 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
OmnipodPacket response = null; OmnipodPacket response = null;
try { try {
response = sendAndListen(packet, responseTimeoutMilliseconds, repeatCount, 9, preambleExtensionMilliseconds, OmnipodPacket.class); response = sendAndListen(packet, responseTimeoutMilliseconds, repeatCount, 9, preambleExtensionMilliseconds, OmnipodPacket.class);
} catch (RileyLinkCommunicationException ex) { } catch (RileyLinkCommunicationException | OmnipodException ex) {
if (isLoggingEnabled()) { if (isLoggingEnabled()) {
LOG.debug("Ignoring exception in exchangePackets", ex); LOG.debug("Ignoring exception in exchangePackets", ex);
} }

View file

@ -8,8 +8,6 @@ import org.slf4j.LoggerFactory;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.L;
@ -33,6 +31,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.command.Canc
import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.StatusResponse; import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.podinfo.PodInfoResponse; import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.podinfo.PodInfoResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.BeepType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.BeepType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.DeliveryType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.DeliveryType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInfoType; import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.SetupProgress; import info.nightscout.androidaps.plugins.pump.omnipod.defs.SetupProgress;
@ -40,11 +39,18 @@ import info.nightscout.androidaps.plugins.pump.omnipod.defs.schedule.BasalSchedu
import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState; import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodStateChangedHandler; import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodStateChangedHandler;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommunicationException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommunicationException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalSetupProgressException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalSetupProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceOutOfSyncException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceOutOfSyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.OmnipodException; import info.nightscout.androidaps.plugins.pump.omnipod.exception.OmnipodException;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst; import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst;
import info.nightscout.androidaps.utils.SP; import info.nightscout.androidaps.utils.SP;
import io.reactivex.Completable;
import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.subjects.SingleSubject;
public class OmnipodManager { public class OmnipodManager {
private static final int ACTION_VERIFICATION_TRIES = 3; private static final int ACTION_VERIFICATION_TRIES = 3;
@ -55,12 +61,18 @@ public class OmnipodManager {
private final PodStateChangedHandler podStateChangedHandler; private final PodStateChangedHandler podStateChangedHandler;
protected PodSessionState podState; protected PodSessionState podState;
private ActiveBolusData activeBolusData;
private final Object bolusDataLock = new Object();
public OmnipodManager(OmnipodCommunicationService communicationService, PodSessionState podState, public OmnipodManager(OmnipodCommunicationService communicationService, PodSessionState podState,
PodStateChangedHandler podStateChangedHandler) { PodStateChangedHandler podStateChangedHandler) {
if (communicationService == null) { if (communicationService == null) {
throw new IllegalArgumentException("Communication service cannot be null"); throw new IllegalArgumentException("Communication service cannot be null");
} }
this.communicationService = communicationService; this.communicationService = communicationService;
if (podState != null) {
podState.setStateChangedHandler(podStateChangedHandler);
}
this.podState = podState; this.podState = podState;
this.podStateChangedHandler = podStateChangedHandler; this.podStateChangedHandler = podStateChangedHandler;
} }
@ -69,27 +81,26 @@ public class OmnipodManager {
this(communicationService, podState, null); this(communicationService, podState, null);
} }
// After priming should have been finished, the pod state is verified. public synchronized Single<SetupActionResult> pairAndPrime() {
// The result of that verification is passed to the SetupActionResultHandler
public void pairAndPrime(SetupActionResultHandler resultHandler) {
if (podState == null) { if (podState == null) {
podState = communicationService.executeAction( podState = communicationService.executeAction(
new PairAction(new PairService(), podStateChangedHandler)); new PairAction(new PairService(), podStateChangedHandler));
} }
if (podState.getSetupProgress().isBefore(SetupProgress.PRIMING_FINISHED)) { if (!podState.getSetupProgress().isBefore(SetupProgress.PRIMING_FINISHED)) {
communicationService.executeAction(new PrimeAction(new PrimeService(), podState));
executeDelayed(() -> verifySetupAction(statusResponse -> PrimeAction.updatePrimingStatus(podState, statusResponse), //
SetupProgress.PRIMING_FINISHED, resultHandler), //
calculateBolusDuration(OmnipodConst.POD_PRIME_BOLUS_UNITS, OmnipodConst.POD_PRIMING_DELIVERY_RATE));
} else {
throw new IllegalSetupProgressException(SetupProgress.ADDRESS_ASSIGNED, podState.getSetupProgress()); throw new IllegalSetupProgressException(SetupProgress.ADDRESS_ASSIGNED, podState.getSetupProgress());
} }
communicationService.executeAction(new PrimeAction(new PrimeService(), podState));
long delayInSeconds = calculateBolusDuration(OmnipodConst.POD_PRIME_BOLUS_UNITS, OmnipodConst.POD_PRIMING_DELIVERY_RATE).getStandardSeconds();
return Single.timer(delayInSeconds, TimeUnit.SECONDS) //
.map(o -> verifySetupAction(statusResponse ->
PrimeAction.updatePrimingStatus(podState, statusResponse), SetupProgress.PRIMING_FINISHED)) //
.observeOn(AndroidSchedulers.mainThread());
} }
// After inserting the cannula should have been finished, the pod state is verified. public synchronized Single<SetupActionResult> insertCannula(BasalSchedule basalSchedule) {
// The result of that verification is passed to the SetupActionResultHandler
public void insertCannula(BasalSchedule basalSchedule, SetupActionResultHandler resultHandler) {
if (podState == null || podState.getSetupProgress().isBefore(SetupProgress.PRIMING_FINISHED)) { if (podState == null || podState.getSetupProgress().isBefore(SetupProgress.PRIMING_FINISHED)) {
throw new IllegalSetupProgressException(SetupProgress.PRIMING_FINISHED, podState == null ? null : podState.getSetupProgress()); throw new IllegalSetupProgressException(SetupProgress.PRIMING_FINISHED, podState == null ? null : podState.getSetupProgress());
} else if (podState.getSetupProgress().isAfter(SetupProgress.CANNULA_INSERTING)) { } else if (podState.getSetupProgress().isAfter(SetupProgress.CANNULA_INSERTING)) {
@ -98,12 +109,14 @@ public class OmnipodManager {
communicationService.executeAction(new InsertCannulaAction(new InsertCannulaService(), podState, basalSchedule)); communicationService.executeAction(new InsertCannulaAction(new InsertCannulaService(), podState, basalSchedule));
executeDelayed(() -> verifySetupAction(statusResponse -> InsertCannulaAction.updateCannulaInsertionStatus(podState, statusResponse), // long delayInSeconds = calculateBolusDuration(OmnipodConst.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConst.POD_CANNULA_INSERTION_DELIVERY_RATE).getStandardSeconds();
SetupProgress.COMPLETED, resultHandler), return Single.timer(delayInSeconds, TimeUnit.SECONDS) //
calculateBolusDuration(OmnipodConst.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConst.POD_CANNULA_INSERTION_DELIVERY_RATE)); .map(o -> verifySetupAction(statusResponse ->
InsertCannulaAction.updateCannulaInsertionStatus(podState, statusResponse), SetupProgress.COMPLETED)) //
.observeOn(AndroidSchedulers.mainThread());
} }
public StatusResponse getPodStatus() { public synchronized StatusResponse getPodStatus() {
if (podState == null) { if (podState == null) {
throw new IllegalSetupProgressException(SetupProgress.PRIMING_FINISHED, null); throw new IllegalSetupProgressException(SetupProgress.PRIMING_FINISHED, null);
} }
@ -111,126 +124,170 @@ public class OmnipodManager {
return communicationService.executeAction(new GetStatusAction(podState)); return communicationService.executeAction(new GetStatusAction(podState));
} }
public PodInfoResponse getPodInfo(PodInfoType podInfoType) { public synchronized PodInfoResponse getPodInfo(PodInfoType podInfoType) {
assertReadyForDelivery(); assertReadyForDelivery();
return communicationService.executeAction(new GetPodInfoAction(podState, podInfoType)); return communicationService.executeAction(new GetPodInfoAction(podState, podInfoType));
} }
public void acknowledgeAlerts() { public synchronized void acknowledgeAlerts() {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new AcknowledgeAlertsAction(podState, podState.getActiveAlerts())); executeAndVerify(() -> communicationService.executeAction(new AcknowledgeAlertsAction(podState, podState.getActiveAlerts())));
} }
public void setBasalSchedule(BasalSchedule schedule) { public synchronized void setBasalSchedule(BasalSchedule schedule, boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new SetBasalScheduleAction(podState, schedule, executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podState, schedule,
false, podState.getScheduleOffset(), true)); false, podState.getScheduleOffset(), acknowledgementBeep)));
} }
public void setTemporaryBasal(TempBasalPair tempBasalPair) { public synchronized void setTemporaryBasal(TempBasalPair tempBasalPair, boolean acknowledgementBeep, boolean completionBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new SetTempBasalAction(new SetTempBasalService(), executeAndVerify(() -> communicationService.executeAction(new SetTempBasalAction(new SetTempBasalService(),
podState, tempBasalPair.getInsulinRate(), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), podState, tempBasalPair.getInsulinRate(), Duration.standardMinutes(tempBasalPair.getDurationMinutes()),
true, true)); acknowledgementBeep, completionBeep)));
} }
public void cancelTemporaryBasal() { public synchronized void cancelTemporaryBasal(boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.TEMP_BASAL, true)); executeAndVerify(() -> communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.TEMP_BASAL, acknowledgementBeep)));
} }
public void bolus(Double units, StatusResponseHandler bolusCompletionHandler) { // 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
// Only throws OmnipodException[certainFailure=false]
public synchronized BolusCommandResult bolus(Double units, boolean acknowledgementBeep, boolean completionBeep, BolusProgressIndicationConsumer progressIndicationConsumer) {
assertReadyForDelivery(); assertReadyForDelivery();
CommandDeliveryStatus commandDeliveryStatus = CommandDeliveryStatus.SUCCESS;
try { try {
communicationService.executeAction(new BolusAction(podState, units, true, true)); executeAndVerify(() -> communicationService.executeAction(new BolusAction(podState, units, acknowledgementBeep, completionBeep)));
} catch (Exception ex) { } catch (OmnipodException ex) {
if (isCertainFailure(ex)) { if (ex.isCertainFailure()) {
throw ex; throw ex;
} else {
CommandVerificationResult verificationResult = verifyCommand();
switch (verificationResult) {
case CERTAIN_FAILURE:
if (ex instanceof OmnipodException) {
((OmnipodException) ex).setCertainFailure(true);
throw ex;
} else {
OmnipodException newException = new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
newException.setCertainFailure(true);
throw newException;
}
case UNCERTAIN_FAILURE:
throw ex;
case SUCCESS:
// Ignore original exception
break;
}
} }
// 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;
} }
if (bolusCompletionHandler != null) { DateTime startDate = DateTime.now().minus(OmnipodConst.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
executeDelayed(() -> {
for (int i = 0; ACTION_VERIFICATION_TRIES > i; i++) { CompositeDisposable disposables = new CompositeDisposable();
StatusResponse statusResponse = null; Duration bolusDuration = calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE);
try { Duration estimatedRemainingBolusDuration = bolusDuration.minus(OmnipodConst.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
statusResponse = getPodStatus();
if (statusResponse.getDeliveryStatus().isBolusing()) { if (progressIndicationConsumer != null) {
break; int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10));
long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports;
disposables.add(Flowable.intervalRange(0, numberOfProgressReports + 1, 0, progressReportInterval, TimeUnit.MILLISECONDS) //
.observeOn(AndroidSchedulers.mainThread()) //
.subscribe(count -> {
int percentage = (int) ((double) count / numberOfProgressReports * 100);
double estimatedUnitsDelivered = activeBolusData == null ? 0 : activeBolusData.estimateUnitsDelivered();
progressIndicationConsumer.accept(estimatedUnitsDelivered, percentage);
}));
}
SingleSubject<BolusDeliveryResult> bolusCompletionSubject = SingleSubject.create();
disposables.add(Completable.complete() //
.delay(estimatedRemainingBolusDuration.getMillis() + 250, TimeUnit.MILLISECONDS) //
.observeOn(AndroidSchedulers.mainThread()) //
.doOnComplete(() -> {
synchronized (bolusDataLock) {
for (int i = 0; i < ACTION_VERIFICATION_TRIES; i++) {
try {
// Retrieve a status response in order to update the pod state
StatusResponse statusResponse = getPodStatus();
if (statusResponse.getDeliveryStatus().isBolusing()) {
throw new IllegalDeliveryStatusException(DeliveryStatus.NORMAL, statusResponse.getDeliveryStatus());
} else {
break;
}
} catch (Exception ex) {
if (isLoggingEnabled()) {
LOG.debug("Ignoring exception in bolus completion verfication", ex);
}
}
}
if (activeBolusData != null) {
activeBolusData.bolusCompletionSubject.onSuccess(new BolusDeliveryResult(units));
activeBolusData = null;
} }
} catch (Exception ex) {
// Ignore
} }
bolusCompletionHandler.handle(statusResponse); })
} .subscribe());
}, calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE));
synchronized (bolusDataLock) {
activeBolusData = new ActiveBolusData(units, startDate, bolusCompletionSubject, disposables);
}
return new BolusCommandResult(commandDeliveryStatus, bolusCompletionSubject);
}
public synchronized void cancelBolus(boolean acknowledgementBeep) {
assertReadyForDelivery();
synchronized (bolusDataLock) {
if (activeBolusData == null) {
throw new IllegalDeliveryStatusException(DeliveryStatus.BOLUS_IN_PROGRESS, podState.getLastDeliveryStatus());
}
executeAndVerify(() -> communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.BOLUS, acknowledgementBeep)));
activeBolusData.getDisposables().dispose();
activeBolusData.getBolusCompletionSubject().onSuccess(new BolusDeliveryResult(activeBolusData.estimateUnitsDelivered()));
activeBolusData = null;
} }
} }
public void cancelBolus() { // CAUTION: cancels TBR and bolus
public synchronized void suspendDelivery(boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new CancelDeliveryAction(podState, DeliveryType.BOLUS, true));
executeAndVerify(() -> communicationService.executeAction(new CancelDeliveryAction(podState, EnumSet.allOf(DeliveryType.class), acknowledgementBeep)));
} }
public void suspendDelivery() { public synchronized void resumeDelivery(boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new CancelDeliveryAction(podState, EnumSet.allOf(DeliveryType.class), true)); executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podState, podState.getBasalSchedule(),
true, podState.getScheduleOffset(), acknowledgementBeep)));
} }
public void resumeDelivery() { // CAUTION: cancels TBR and bolus
// CAUTION: suspends and then resumes delivery.
// If any error occurs during the command sequence, delivery could be suspended
public synchronized void setTime(boolean acknowledgementBeeps) {
assertReadyForDelivery(); assertReadyForDelivery();
communicationService.executeAction(new SetBasalScheduleAction(podState, podState.getBasalSchedule(), suspendDelivery(acknowledgementBeeps);
true, podState.getScheduleOffset(), true));
}
// If this command fails, it it possible that delivery has been suspended
public void setTime() {
assertReadyForDelivery();
// Suspend delivery
communicationService.executeAction(new CancelDeliveryAction(podState, EnumSet.allOf(DeliveryType.class), false));
// Joda seems to cache the default time zone, so we use the JVM's // Joda seems to cache the default time zone, so we use the JVM's
DateTimeZone.setDefault(DateTimeZone.forTimeZone(TimeZone.getDefault())); DateTimeZone.setDefault(DateTimeZone.forTimeZone(TimeZone.getDefault()));
podState.setTimeZone(DateTimeZone.getDefault()); podState.setTimeZone(DateTimeZone.getDefault());
// Resume delivery resumeDelivery(acknowledgementBeeps);
StatusResponse statusResponse = communicationService.executeAction(new SetBasalScheduleAction(podState, podState.getBasalSchedule(),
true, podState.getScheduleOffset(), true));
} }
public void deactivatePod() { public synchronized void deactivatePod(boolean acknowledgementBeep) {
if (podState == null) { if (podState == null) {
throw new IllegalSetupProgressException(SetupProgress.ADDRESS_ASSIGNED, null); throw new IllegalSetupProgressException(SetupProgress.ADDRESS_ASSIGNED, null);
} }
communicationService.executeAction(new DeactivatePodAction(podState, true)); executeAndVerify(() -> communicationService.executeAction(new DeactivatePodAction(podState, acknowledgementBeep)));
resetPodState(); resetPodState();
} }
@ -259,9 +316,33 @@ public class OmnipodManager {
return podState == null ? "null" : podState.toString(); return podState == null ? "null" : podState.toString();
} }
private void executeDelayed(Runnable r, Duration timeout) { // Only works for commands with nonce resyncable message blocks
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); private void executeAndVerify(Runnable runnable) {
scheduledExecutorService.schedule(r, timeout.getMillis(), TimeUnit.MILLISECONDS); try {
runnable.run();
} catch (Exception ex) {
if (isCertainFailure(ex)) {
throw ex;
} else {
CommandDeliveryStatus verificationResult = verifyCommand();
switch (verificationResult) {
case CERTAIN_FAILURE:
if (ex instanceof OmnipodException) {
((OmnipodException) ex).setCertainFailure(true);
throw ex;
} else {
OmnipodException newException = new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
newException.setCertainFailure(true);
throw newException;
}
case UNCERTAIN_FAILURE:
throw ex;
case SUCCESS:
// Ignore original exception
break;
}
}
}
} }
private void assertReadyForDelivery() { private void assertReadyForDelivery() {
@ -270,12 +351,12 @@ public class OmnipodManager {
} }
} }
private void verifySetupAction(StatusResponseHandler setupActionResponseHandler, SetupProgress expectedSetupProgress, SetupActionResultHandler resultHandler) { 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);
@ -290,13 +371,11 @@ public class OmnipodManager {
.exception(ex); .exception(ex);
} }
} }
if (resultHandler != null) { return result;
resultHandler.handle(result);
}
} }
// 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");
} }
@ -305,14 +384,14 @@ public class OmnipodManager {
new CancelDeliveryCommand(podState.getCurrentNonce(), BeepType.NO_BEEP, DeliveryType.NONE), false); new CancelDeliveryCommand(podState.getCurrentNonce(), BeepType.NO_BEEP, DeliveryType.NONE), false);
} catch (NonceOutOfSyncException ex) { } catch (NonceOutOfSyncException ex) {
if (isLoggingEnabled()) { if (isLoggingEnabled()) {
LOG.info("Command resolved to FAILURE (CERTAIN_FAILURE)"); LOG.info("Command resolved to FAILURE (CERTAIN_FAILURE)", ex);
} }
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)", ex);
} }
return CommandVerificationResult.UNCERTAIN_FAILURE; return CommandDeliveryStatus.UNCERTAIN_FAILURE;
} }
if (isLoggingEnabled()) { if (isLoggingEnabled()) {
@ -320,28 +399,110 @@ 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() {
return L.isEnabled(L.PUMP); return L.isEnabled(L.PUMP);
} }
public static Duration calculateBolusDuration(double units) {
return calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE);
}
private static Duration calculateBolusDuration(double units, double deliveryRate) { private static Duration calculateBolusDuration(double units, double deliveryRate) {
return Duration.standardSeconds((long) Math.ceil(units / deliveryRate)); return Duration.standardSeconds((long) Math.ceil(units / deliveryRate));
} }
public static Duration calculateBolusDuration(double units) {
return calculateBolusDuration(units, OmnipodConst.POD_BOLUS_DELIVERY_RATE);
}
public static boolean isCertainFailure(Exception ex) { public static boolean isCertainFailure(Exception ex) {
return ex instanceof OmnipodException && ((OmnipodException) ex).isCertainFailure(); return ex instanceof OmnipodException && ((OmnipodException) ex).isCertainFailure();
} }
private enum CommandVerificationResult { 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;
public BolusDeliveryResult(double unitsDelivered) {
this.unitsDelivered = unitsDelivered;
}
public double getUnitsDelivered() {
return unitsDelivered;
}
}
public enum CommandDeliveryStatus {
SUCCESS, SUCCESS,
CERTAIN_FAILURE, CERTAIN_FAILURE,
UNCERTAIN_FAILURE UNCERTAIN_FAILURE
} }
// TODO replace with Consumer when our min API level >= 24
@FunctionalInterface
private interface StatusResponseConsumer {
void accept(StatusResponse statusResponse);
}
private static class ActiveBolusData {
private final double units;
private volatile DateTime startDate;
private volatile SingleSubject<BolusDeliveryResult> bolusCompletionSubject;
private volatile CompositeDisposable disposables;
private ActiveBolusData(double units, DateTime startDate, SingleSubject<BolusDeliveryResult> bolusCompletionSubject, CompositeDisposable disposables) {
this.units = units;
this.startDate = startDate;
this.bolusCompletionSubject = bolusCompletionSubject;
this.disposables = disposables;
}
public double getUnits() {
return units;
}
public DateTime getStartDate() {
return startDate;
}
public CompositeDisposable getDisposables() {
return disposables;
}
public SingleSubject<BolusDeliveryResult> getBolusCompletionSubject() {
return bolusCompletionSubject;
}
public void setBolusCompletionSubject(SingleSubject<BolusDeliveryResult> bolusCompletionSubject) {
this.bolusCompletionSubject = bolusCompletionSubject;
}
public double estimateUnitsDelivered() {
// TODO this needs improvement
// take (average) radio communication time into account
long elapsedMillis = new Duration(startDate, DateTime.now()).getMillis();
long totalDurationMillis = (long) (units / OmnipodConst.POD_BOLUS_DELIVERY_RATE * 1000);
double factor = (double) elapsedMillis / totalDurationMillis;
double estimatedUnits = Math.min(1D, factor) * units;
int roundingDivisor = (int) (1 / OmnipodConst.POD_PULSE_SIZE);
return (double) Math.round(estimatedUnits * roundingDivisor) / roundingDivisor;
}
}
} }

View file

@ -1,6 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
@FunctionalInterface
public interface SetupActionResultHandler {
void handle(SetupActionResult result);
}

View file

@ -1,8 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.response.StatusResponse;
@FunctionalInterface
public interface StatusResponseHandler {
void handle(StatusResponse statusResponse);
}

View file

@ -50,7 +50,8 @@ public class PairAction implements OmnipodAction<PodSessionState> {
PodSessionState podState = new PodSessionState(timeZone, address, activationDate, confirmPairingResponse.getPiVersion(), PodSessionState podState = new PodSessionState(timeZone, address, activationDate, confirmPairingResponse.getPiVersion(),
confirmPairingResponse.getPmVersion(), confirmPairingResponse.getLot(), confirmPairingResponse.getTid(), confirmPairingResponse.getPmVersion(), confirmPairingResponse.getLot(), confirmPairingResponse.getTid(),
setupState.getPacketNumber(), setupState.getMessageNumber(), podStateChangedHandler); setupState.getPacketNumber(), setupState.getMessageNumber());
podState.setStateChangedHandler(podStateChangedHandler);
podState.setSetupProgress(SetupProgress.POD_CONFIGURED); podState.setSetupProgress(SetupProgress.POD_CONFIGURED);
return podState; return podState;

View file

@ -40,7 +40,7 @@ public interface OmnipodCommunicationManagerInterface {
* *
* @param amount amount of bolus in U * @param amount amount of bolus in U
*/ */
PumpEnactResult setBolus(Double amount); PumpEnactResult setBolus(Double amount, boolean isSmb);
/** /**
* Cancel Bolus (if bolus is already stopped, return acknowledgment) * Cancel Bolus (if bolus is already stopped, return acknowledgment)

View file

@ -26,7 +26,7 @@ import info.nightscout.androidaps.utils.SP;
public class PodSessionState extends PodState { public class PodSessionState extends PodState {
private final Map<AlertSlot, AlertType> configuredAlerts; private final Map<AlertSlot, AlertType> configuredAlerts;
private final PodStateChangedHandler stateChangedHandler; private transient PodStateChangedHandler stateChangedHandler;
private DateTime activatedAt; private DateTime activatedAt;
private DateTime expiresAt; private DateTime expiresAt;
private final FirmwareVersion piVersion; private final FirmwareVersion piVersion;
@ -43,8 +43,7 @@ public class PodSessionState extends PodState {
private DeliveryStatus lastDeliveryStatus; private DeliveryStatus lastDeliveryStatus;
public PodSessionState(DateTimeZone timeZone, int address, DateTime activatedAt, FirmwareVersion piVersion, public PodSessionState(DateTimeZone timeZone, int address, DateTime activatedAt, FirmwareVersion piVersion,
FirmwareVersion pmVersion, int lot, int tid, int packetNumber, int messageNumber, FirmwareVersion pmVersion, int lot, int tid, int packetNumber, int messageNumber) {
PodStateChangedHandler stateChangedHandler) {
super(address, messageNumber, packetNumber); super(address, messageNumber, packetNumber);
if (timeZone == null) { if (timeZone == null) {
throw new IllegalArgumentException("Time zone can not be null"); throw new IllegalArgumentException("Time zone can not be null");
@ -61,11 +60,18 @@ public class PodSessionState extends PodState {
this.pmVersion = pmVersion; this.pmVersion = pmVersion;
this.lot = lot; this.lot = lot;
this.tid = tid; this.tid = tid;
this.stateChangedHandler = stateChangedHandler;
this.nonceState = new NonceState(lot, tid); this.nonceState = new NonceState(lot, tid);
store(); store();
} }
public void setStateChangedHandler(PodStateChangedHandler handler) {
// FIXME this is an ugly workaround for not being able to serialize the PodStateChangedHandler
if(stateChangedHandler != null) {
throw new IllegalStateException("A PodStateChangedHandler has already been already registered");
}
stateChangedHandler = handler;
}
public AlertType getConfiguredAlertType(AlertSlot alertSlot) { public AlertType getConfiguredAlertType(AlertSlot alertSlot) {
return configuredAlerts.get(alertSlot); return configuredAlerts.get(alertSlot);
} }

View file

@ -13,6 +13,8 @@ import info.nightscout.androidaps.R;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.bus.RxBus;
import info.nightscout.androidaps.plugins.general.overview.events.EventOverviewBolusProgress;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair; import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationService; import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodCommunicationService;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.comm.OmnipodManager;
@ -45,6 +47,7 @@ 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.disposables.Disposable;
public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface { public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface {
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP); private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
@ -70,7 +73,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
public PumpEnactResult initPod(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver, Profile profile) { public PumpEnactResult initPod(PodInitActionType podInitActionType, PodInitReceiver podInitReceiver, Profile profile) {
if (PodInitActionType.PairAndPrimeWizardStep.equals(podInitActionType)) { if (PodInitActionType.PairAndPrimeWizardStep.equals(podInitActionType)) {
try { try {
delegate.pairAndPrime(res -> // Disposable disposable = delegate.pairAndPrime().subscribe(res -> //
handleSetupActionResult(podInitActionType, podInitReceiver, res)); handleSetupActionResult(podInitActionType, podInitReceiver, res));
return new PumpEnactResult().success(true).enacted(true); return new PumpEnactResult().success(true).enacted(true);
} catch (Exception ex) { } catch (Exception ex) {
@ -80,7 +83,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
} }
} else if (PodInitActionType.FillCannulaSetBasalProfileWizardStep.equals(podInitActionType)) { } else if (PodInitActionType.FillCannulaSetBasalProfileWizardStep.equals(podInitActionType)) {
try { try {
delegate.insertCannula(mapProfileToBasalSchedule(profile), res -> // Disposable disposable = delegate.insertCannula(mapProfileToBasalSchedule(profile)).subscribe(res -> //
handleSetupActionResult(podInitActionType, podInitReceiver, res)); handleSetupActionResult(podInitActionType, podInitReceiver, res));
return new PumpEnactResult().success(true).enacted(true); return new PumpEnactResult().success(true).enacted(true);
} catch (Exception ex) { } catch (Exception ex) {
@ -107,7 +110,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
@Override @Override
public PumpEnactResult deactivatePod(PodInitReceiver podInitReceiver) { public PumpEnactResult deactivatePod(PodInitReceiver podInitReceiver) {
try { try {
delegate.deactivatePod(); delegate.deactivatePod(true);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
podInitReceiver.returnInitTaskStatus(PodInitActionType.DeactivatePodWizardStep, false, comment); podInitReceiver.returnInitTaskStatus(PodInitActionType.DeactivatePodWizardStep, false, comment);
@ -124,7 +127,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
@Override @Override
public PumpEnactResult setBasalProfile(Profile basalProfile) { public PumpEnactResult setBasalProfile(Profile basalProfile) {
try { try {
delegate.setBasalSchedule(mapProfileToBasalSchedule(basalProfile)); delegate.setBasalSchedule(mapProfileToBasalSchedule(basalProfile), isBasalBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -145,36 +148,49 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
} }
@Override @Override
public PumpEnactResult setBolus(Double amount) { public PumpEnactResult setBolus(Double units, boolean isSmb) {
OmnipodManager.BolusCommandResult bolusCommandResult;
boolean beepsEnabled = isSmb ? isSmbBeepsEnabled() : isBolusBeepsEnabled();
try { try {
delegate.bolus(amount, statusResponse -> { bolusCommandResult = delegate.bolus(units, beepsEnabled, beepsEnabled, isSmb ? null :
if (statusResponse == null) { (estimatedUnitsDelivered, percentage) -> {
// Failed to retrieve status response after bolus EventOverviewBolusProgress progressUpdateEvent = EventOverviewBolusProgress.INSTANCE;
// Bolus probably finished anyway progressUpdateEvent.setStatus(getStringResource(R.string.bolusdelivering, units));
} else if (statusResponse.getDeliveryStatus().isBolusing()) { progressUpdateEvent.setPercent(percentage);
// This shouldn't happen RxBus.INSTANCE.send(progressUpdateEvent);
} else { });
// Bolus successfully completed
}
});
} catch (Exception ex) { } catch (Exception ex) {
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
return new PumpEnactResult().success(true).enacted(true); // For safety reasons, we should treat this as a bolus that has been delivered, in order to prevent insulin overdose
}
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); return new PumpEnactResult().success(true).enacted(true).bolusDelivered(unitsDelivered);
} }
@Override @Override
public PumpEnactResult cancelBolus() { public PumpEnactResult cancelBolus() {
try { try {
delegate.cancelBolus(); delegate.cancelBolus(isBolusBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -185,8 +201,9 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
@Override @Override
public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) { public PumpEnactResult setTemporaryBasal(TempBasalPair tempBasalPair) {
boolean beepsEnabled = isBasalBeepsEnabled();
try { try {
delegate.setTemporaryBasal(tempBasalPair); delegate.setTemporaryBasal(tempBasalPair, beepsEnabled, beepsEnabled);
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -198,7 +215,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
@Override @Override
public PumpEnactResult cancelTemporaryBasal() { public PumpEnactResult cancelTemporaryBasal() {
try { try {
delegate.cancelTemporaryBasal(); delegate.cancelTemporaryBasal(isBasalBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -241,7 +258,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
public PumpEnactResult suspendDelivery() { public PumpEnactResult suspendDelivery() {
try { try {
delegate.suspendDelivery(); delegate.suspendDelivery(isBasalBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -252,7 +269,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
public PumpEnactResult resumeDelivery() { public PumpEnactResult resumeDelivery() {
try { try {
delegate.resumeDelivery(); delegate.resumeDelivery(isBasalBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
@ -264,8 +281,10 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
// TODO should we add this to the OmnipodCommunicationManager interface? // TODO should we add this to the OmnipodCommunicationManager interface?
public PumpEnactResult setTime() { public PumpEnactResult setTime() {
try { try {
delegate.setTime(); // CAUTION cancels TBR
delegate.setTime(isBasalBeepsEnabled());
} catch (Exception ex) { } catch (Exception ex) {
// CAUTION pod could be suspended
String comment = handleAndTranslateException(ex); String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment); return new PumpEnactResult().success(false).enacted(false).comment(comment);
} }
@ -301,7 +320,9 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
String comment = null; String comment = null;
switch (res.getResultType()) { switch (res.getResultType()) {
case FAILURE: case FAILURE:
LOG.error("Setup action failed: illegal setup progress: {}", res.getSetupProgress()); if (loggingEnabled()) {
LOG.error("Setup action failed: illegal setup progress: {}", res.getSetupProgress());
}
comment = getStringResource(R.string.omnipod_driver_error_invalid_progress_state, res.getSetupProgress()); comment = getStringResource(R.string.omnipod_driver_error_invalid_progress_state, res.getSetupProgress());
break; break;
case VERIFICATION_FAILURE: case VERIFICATION_FAILURE:
@ -362,6 +383,21 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
return comment; return comment;
} }
private boolean isBolusBeepsEnabled() {
// TODO
return true;
}
private boolean isSmbBeepsEnabled() {
// TODO
return true;
}
private boolean isBasalBeepsEnabled() {
// TODO
return true;
}
private String getStringResource(int id, Object... args) { private String getStringResource(int id, Object... args) {
return MainApp.gs(id, args); return MainApp.gs(id, args);
} }

View file

@ -31,7 +31,7 @@ public class OmnipodUIComm {
} }
public synchronized OmnipodUITask executeCommand(OmnipodCommandType commandType, Object... parameters) { public OmnipodUITask executeCommand(OmnipodCommandType commandType, Object... parameters) {
if (isLogEnabled()) if (isLogEnabled())
LOG.warn("Execute Command: " + commandType.name()); LOG.warn("Execute Command: " + commandType.name());

View file

@ -80,9 +80,10 @@ public class OmnipodUITask {
case SetBolus: { case SetBolus: {
Double amount = getDoubleFromParameters(0); Double amount = getDoubleFromParameters(0);
boolean isSmb = getBooleanFromParameters(1);
if (amount != null) if (amount != null)
returnData = communicationManager.setBolus(amount); returnData = communicationManager.setBolus(amount, isSmb);
// TODO returnData = communicationManager.bolus(amount); // TODO returnData = communicationManager.bolus(amount);
} }
break; break;
@ -138,6 +139,9 @@ public class OmnipodUITask {
return (Double) parameters[index]; return (Double) parameters[index];
} }
public boolean getBooleanFromParameters(int index) {
return (boolean) parameters[index];
}
public Integer getIntegerFromParameters(int index) { public Integer getIntegerFromParameters(int index) {
return (Integer) parameters[index]; return (Integer) parameters[index];

View file

@ -35,6 +35,8 @@ 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.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);
public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1); public static final Duration END_OF_SERVICE_IMMINENT_WINDOW = Duration.standardHours(1);

View file

@ -85,7 +85,7 @@ public class OmnipodDashCommunicationManager implements OmnipodCommunicationMana
return null; return null;
} }
public PumpEnactResult setBolus(Double parameter) { public PumpEnactResult setBolus(Double parameter, boolean isSmb) {
return null; return null;
} }

View file

@ -1667,7 +1667,7 @@
<string name="omnipod_error_illegal_init_action_type">Illegal PodInitActionType: %1$s</string> <string name="omnipod_error_illegal_init_action_type">Illegal PodInitActionType: %1$s</string>
<string name="omnipod_driver_error_setup_action_verification_failed">Command verification failed.</string> <string name="omnipod_driver_error_setup_action_verification_failed">Command verification failed.</string>
<string name="omnipod_driver_error_unexpected_exception_type">An unexpected error occured. Please report! (type: %1$d).</string> <string name="omnipod_driver_error_unexpected_exception_type">An unexpected error occured. Please report! (type: %1$s).</string>
<string name="omnipod_driver_error_invalid_parameters">Communication failed: received invalid input parameters.</string> <string name="omnipod_driver_error_invalid_parameters">Communication failed: received invalid input parameters.</string>
<string name="omnipod_driver_error_communication_failed">Communication failed.</string> <string name="omnipod_driver_error_communication_failed">Communication failed.</string>
<string name="omnipod_driver_error_crc_mismatch">Communication failed: Message integrity verification failed.</string> <string name="omnipod_driver_error_crc_mismatch">Communication failed: Message integrity verification failed.</string>

View file

@ -8,7 +8,7 @@ buildscript {
maven { url 'https://maven.fabric.io/public' } maven { url 'https://maven.fabric.io/public' }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.5.2' classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.google.gms:google-services:4.3.3' classpath 'com.google.gms:google-services:4.3.3'
classpath 'io.fabric.tools:gradle:1.31.2' classpath 'io.fabric.tools:gradle:1.31.2'