Merge branch 'omnipod_eros' of https://github.com/AAPS-Omnipod/AndroidAPS into omnipod_eros
This commit is contained in:
commit
73f1aaf819
16 changed files with 389 additions and 170 deletions
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.pump.omnipod.comm;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface SetupActionResultHandler {
|
|
||||||
void handle(SetupActionResult result);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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'
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue