Add verification mechanism for Omnipod bolus

This commit is contained in:
Bart Sopers 2019-11-30 22:51:49 +01:00
parent 8dcadd7a35
commit 27cf316dc7
21 changed files with 170 additions and 103 deletions

View file

@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.List;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkCommunicationManager;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RFSpy;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.RileyLinkCommunicationException;
@ -27,13 +28,14 @@ import info.nightscout.androidaps.plugins.pump.omnipod.defs.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodState;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommunicationException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalResponseException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceOutOfSyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceResyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NotEnoughDataException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.OmnipodException;
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.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalResponseException;
/**
* Created by andy on 6/29/18.
@ -41,7 +43,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalResponse
public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
private static final Logger LOG = LoggerFactory.getLogger(OmnipodCommunicationService.class);
private static final Logger LOG = LoggerFactory.getLogger(L.PUMPCOMM);
public OmnipodCommunicationService(RFSpy rfspy) {
super(rfspy);
@ -73,8 +75,12 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
}
public <T extends MessageBlock> T sendCommand(Class<T> responseClass, PodState podState, MessageBlock command) {
return sendCommand(responseClass, podState, command, true);
}
public <T extends MessageBlock> T sendCommand(Class<T> responseClass, PodState podState, MessageBlock command, boolean automaticallyResyncNone) {
OmnipodMessage message = new OmnipodMessage(podState.getAddress(), Collections.singletonList(command), podState.getMessageNumber());
return exchangeMessages(responseClass, podState, message);
return exchangeMessages(responseClass, podState, message, automaticallyResyncNone);
}
// Convenience method
@ -83,10 +89,18 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
}
public <T extends MessageBlock> T exchangeMessages(Class<T> responseClass, PodState podState, OmnipodMessage message) {
return exchangeMessages(responseClass, podState, message, null, null);
return exchangeMessages(responseClass, podState, message, true);
}
public <T extends MessageBlock> T exchangeMessages(Class<T> responseClass, PodState podState, OmnipodMessage message, boolean automaticallyResyncNonce) {
return exchangeMessages(responseClass, podState, message, null, null, automaticallyResyncNonce);
}
public synchronized <T extends MessageBlock> T exchangeMessages(Class<T> responseClass, PodState podState, OmnipodMessage message, Integer addressOverride, Integer ackAddressOverride) {
return exchangeMessages(responseClass, podState, message, addressOverride, ackAddressOverride, true);
}
public synchronized <T extends MessageBlock> T exchangeMessages(Class<T> responseClass, PodState podState, OmnipodMessage message, Integer addressOverride, Integer ackAddressOverride, boolean automaticallyResyncNonce) {
for (int i = 0; 2 > i; i++) {
if (podState.hasNonceState() && message.isNonceResyncable()) {
@ -106,13 +120,16 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
ErrorResponse error = (ErrorResponse) responseMessageBlock;
if (error.getErrorResponseType() == ErrorResponseType.BAD_NONCE) {
podState.resyncNonce(error.getNonceSearchKey(), message.getSentNonce(), message.getSequenceNumber());
message.resyncNonce(podState.getCurrentNonce());
if (automaticallyResyncNonce) {
message.resyncNonce(podState.getCurrentNonce());
} else {
throw new NonceOutOfSyncException();
}
} else {
throw new PodReturnedErrorResponseException((ErrorResponse) responseMessageBlock);
}
} else if (responseMessageBlock.getType() == MessageBlockType.POD_INFO_RESPONSE && ((PodInfoResponse) responseMessageBlock).getSubType() == PodInfoType.FAULT_EVENT) {
PodInfoFaultEvent faultEvent = ((PodInfoResponse) responseMessageBlock).getPodInfo();
LOG.error("Pod fault: " + faultEvent.getFaultEventCode().name());
podState.setFaultEvent(faultEvent);
throw new PodFaultException(faultEvent);
} else {
@ -189,7 +206,10 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
if (messageBlocks.size() == 0) {
throw new NotEnoughDataException(receivedMessageData);
} else if (messageBlocks.size() > 1) {
LOG.error("received more than one message block: " + messageBlocks.toString());
// BS: don't expect this to happen
if (isLoggingEnabled()) {
LOG.error("Received more than one message block: {}", messageBlocks.toString());
}
}
return messageBlocks.get(0);
@ -216,7 +236,13 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
if (RileyLinkBLEError.Timeout.equals(ex.getErrorCode())) {
quiet = true;
} else {
LOG.debug("Ignoring exception in ackUntilQuiet: " + ex.getClass().getSimpleName() + ": " + ex.getErrorCode() + ": " + ex.getMessage());
if (isLoggingEnabled()) {
LOG.debug("Ignoring exception in ackUntilQuiet", ex);
}
}
} catch (OmnipodException ex) {
if (isLoggingEnabled()) {
LOG.debug("Ignoring exception in ackUntilQuiet", ex);
}
} catch (Exception ex) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
@ -241,7 +267,9 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
try {
response = sendAndListen(packet, responseTimeoutMilliseconds, repeatCount, 9, preambleExtensionMilliseconds, OmnipodPacket.class);
} catch (RileyLinkCommunicationException ex) {
LOG.debug("Ignoring exception in exchangePackets: " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
if (isLoggingEnabled()) {
LOG.debug("Ignoring exception in exchangePackets", ex);
}
} catch (Exception ex) {
throw new CommunicationException(CommunicationException.Type.UNEXPECTED_EXCEPTION, ex);
}
@ -260,4 +288,8 @@ public class OmnipodCommunicationService extends RileyLinkCommunicationManager {
}
throw new CommunicationException(CommunicationException.Type.TIMEOUT);
}
private boolean isLoggingEnabled() {
return L.isEnabled(L.PUMPCOMM);
}
}

View file

@ -3,6 +3,8 @@ package info.nightscout.androidaps.plugins.pump.omnipod.comm;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.EnumSet;
import java.util.TimeZone;
@ -10,6 +12,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import info.nightscout.androidaps.logging.L;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.AcknowledgeAlertsAction;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.BolusAction;
@ -26,20 +29,27 @@ import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.service.Inser
import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.service.PairService;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.service.PrimeService;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.action.service.SetTempBasalService;
import info.nightscout.androidaps.plugins.pump.omnipod.comm.message.command.CancelDeliveryCommand;
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.defs.BeepType;
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.SetupProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.defs.state.PodSessionState;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.CommunicationException;
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.OmnipodException;
import info.nightscout.androidaps.plugins.pump.omnipod.util.OmnipodConst;
import info.nightscout.androidaps.utils.SP;
public class OmnipodManager {
private static final int ACTION_VERIFICATION_TRIES = 3;
private static final Logger LOG = LoggerFactory.getLogger(L.PUMP);
protected final OmnipodCommunicationService communicationService;
protected PodSessionState podState;
@ -128,7 +138,31 @@ public class OmnipodManager {
public void bolus(Double units, StatusResponseHandler bolusCompletionHandler) {
assertReadyForDelivery();
communicationService.executeAction(new BolusAction(podState, units, true, true));
try {
communicationService.executeAction(new BolusAction(podState, units, true, true));
} catch (Exception ex) {
if (isCertainFailure(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;
}
}
}
if (bolusCompletionHandler != null) {
executeDelayed(() -> {
@ -255,4 +289,46 @@ public class OmnipodManager {
resultHandler.handle(result);
}
}
// Only works for commands which contain nonce resyncable message blocks
private CommandVerificationResult verifyCommand() {
if (isLoggingEnabled()) {
LOG.warn("Verifying command by using cancel none command to verify nonce");
}
try {
communicationService.sendCommand(StatusResponse.class, podState,
new CancelDeliveryCommand(podState.getCurrentNonce(), BeepType.NO_BEEP, DeliveryType.NONE), false);
} catch (NonceOutOfSyncException ex) {
if (isLoggingEnabled()) {
LOG.info("Command resolved to FAILURE (CERTAIN_FAILURE)");
}
return CommandVerificationResult.CERTAIN_FAILURE;
} catch (Exception ex) {
if (isLoggingEnabled()) {
LOG.error("Command unresolved (UNCERTAIN_FAILURE)");
}
return CommandVerificationResult.UNCERTAIN_FAILURE;
}
if (isLoggingEnabled()) {
if (isLoggingEnabled()) {
LOG.info("Command status resolved to SUCCESS");
}
}
return CommandVerificationResult.SUCCESS;
}
public static boolean isCertainFailure(Exception ex) {
return ex instanceof OmnipodException && ((OmnipodException) ex).isCertainFailure();
}
private enum CommandVerificationResult {
SUCCESS,
CERTAIN_FAILURE,
UNCERTAIN_FAILURE
}
private boolean isLoggingEnabled() {
return L.isEnabled(L.PUMP);
}
}

View file

@ -40,6 +40,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalPodProgr
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalResponseException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.IllegalSetupProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.MessageDecodingException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceOutOfSyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NonceResyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.NotEnoughDataException;
import info.nightscout.androidaps.plugins.pump.omnipod.exception.OmnipodException;
@ -159,7 +160,13 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
});
} catch (Exception ex) {
String comment = handleAndTranslateException(ex);
return new PumpEnactResult().success(false).enacted(false).comment(comment);
if(OmnipodManager.isCertainFailure(ex)) {
return new PumpEnactResult().success(false).enacted(false).comment(comment);
} else {
// TODO notify user about uncertain failure
// we don't know if the bolus failed, so for safety reasons, we choose to register the bolus as succesful.
return new PumpEnactResult().success(true).enacted(true);
}
}
return new PumpEnactResult().success(true).enacted(true);
@ -328,6 +335,8 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
comment = getStringResource(R.string.omnipod_driver_error_invalid_response);
} else if (ex instanceof MessageDecodingException) {
comment = getStringResource(R.string.omnipod_driver_error_message_decoding_failed);
} else if (ex instanceof NonceOutOfSyncException) {
comment = getStringResource(R.string.omnipod_driver_error_nonce_out_of_sync);
} else if (ex instanceof NonceResyncException) {
comment = getStringResource(R.string.omnipod_driver_error_nonce_resync_failed);
} else if (ex instanceof NotEnoughDataException) {
@ -347,7 +356,7 @@ public class AapsOmnipodManager implements OmnipodCommunicationManagerInterface
} else {
comment = getStringResource(R.string.omnipod_driver_error_unexpected_exception_type, ex.getClass().getName());
if (loggingEnabled()) {
LOG.error(String.format("Caught unexpected exception type from OmnipodManager (user-friendly error message: %s)", comment), ex);
LOG.error(String.format("Caught unexpected exception type[certainFailure=false] from OmnipodManager (user-friendly error message: %s)", comment), ex);
}
}

View file

@ -2,11 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public class ActionInitializationException extends OmnipodException {
public ActionInitializationException(String message) {
super(message);
}
@Override
public boolean isCertainFailure() {
return true;
super(message, true);
}
}

View file

@ -2,11 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public class CommandInitializationException extends OmnipodException {
public CommandInitializationException(String message) {
super(message);
}
@Override
public boolean isCertainFailure() {
return true;
super(message, true);
}
}

View file

@ -4,12 +4,12 @@ public class CommunicationException extends OmnipodException {
private final Type type;
public CommunicationException(Type type) {
super(type.getDescription());
super(type.getDescription(), false);
this.type = type;
}
public CommunicationException(Type type, Throwable cause) {
super(type.getDescription(), cause);
super(type.getDescription() + ": "+ cause, cause, false);
this.type = type;
}
@ -17,11 +17,6 @@ public class CommunicationException extends OmnipodException {
return type;
}
@Override
public boolean isCertainFailure() {
return false;
}
public enum Type {
TIMEOUT("Communication timeout"),
UNEXPECTED_EXCEPTION("Caught an unexpected Exception");

View file

@ -7,7 +7,7 @@ public class CrcMismatchException extends OmnipodException {
private final int actual;
public CrcMismatchException(int expected, int actual) {
super(String.format(Locale.getDefault(), "CRC mismatch: expected %d, got %d", expected, actual));
super(String.format(Locale.getDefault(), "CRC mismatch: expected %d, got %d", expected, actual), false);
this.expected = expected;
this.actual = actual;
}
@ -19,9 +19,4 @@ public class CrcMismatchException extends OmnipodException {
public int getActual() {
return actual;
}
@Override
public boolean isCertainFailure() {
return false;
}
}

View file

@ -10,7 +10,7 @@ public class IllegalDeliveryStatusException extends OmnipodException {
private final DeliveryStatus actual;
public IllegalDeliveryStatusException(DeliveryStatus expected, DeliveryStatus actual) {
super(String.format(Locale.getDefault(), "Illegal delivery status: %s, expected: %s", actual, expected));
super(String.format(Locale.getDefault(), "Illegal delivery status: %s, expected: %s", actual, expected), true);
this.expected = expected;
this.actual = actual;
}
@ -22,9 +22,4 @@ public class IllegalDeliveryStatusException extends OmnipodException {
public DeliveryStatus getActual() {
return actual;
}
@Override
public boolean isCertainFailure() {
return true;
}
}

View file

@ -10,7 +10,7 @@ public class IllegalPacketTypeException extends OmnipodException {
public IllegalPacketTypeException(PacketType expected, PacketType actual) {
super(String.format(Locale.getDefault(), "Illegal packet type: %s, expected %s",
actual, expected));
actual, expected), false);
this.expected = expected;
this.actual = actual;
}
@ -23,8 +23,4 @@ public class IllegalPacketTypeException extends OmnipodException {
return actual;
}
@Override
public boolean isCertainFailure() {
return false;
}
}

View file

@ -9,7 +9,7 @@ public class IllegalPodProgressException extends OmnipodException {
private final PodProgressStatus actual;
public IllegalPodProgressException(PodProgressStatus expected, PodProgressStatus actual) {
super(String.format(Locale.getDefault(), "Illegal setup state: %s, expected: %s", actual, expected));
super(String.format(Locale.getDefault(), "Illegal setup state: %s, expected: %s", actual, expected), true);
this.expected = expected;
this.actual = actual;
}
@ -21,9 +21,4 @@ public class IllegalPodProgressException extends OmnipodException {
public PodProgressStatus getActual() {
return actual;
}
@Override
public boolean isCertainFailure() {
return true;
}
}

View file

@ -10,7 +10,7 @@ public class IllegalResponseException extends OmnipodException {
public IllegalResponseException(String actualClass, MessageBlockType expectedType) {
super(String.format(Locale.getDefault(), "Illegal response type: got class of type %s " +
"for message block type %s", actualClass, expectedType));
"for message block type %s", actualClass, expectedType), false);
this.actualClass = actualClass;
this.expectedType = expectedType;
}
@ -22,9 +22,4 @@ public class IllegalResponseException extends OmnipodException {
public MessageBlockType getExpectedType() {
return expectedType;
}
@Override
public boolean isCertainFailure() {
return false;
}
}

View file

@ -9,7 +9,7 @@ public class IllegalSetupProgressException extends OmnipodException {
private final SetupProgress actual;
public IllegalSetupProgressException(SetupProgress expected, SetupProgress actual) {
super(String.format(Locale.getDefault(), "Illegal setup progress: %s, expected: %s", actual, expected));
super(String.format(Locale.getDefault(), "Illegal setup progress: %s, expected: %s", actual, expected), true);
this.expected = expected;
this.actual = actual;
}
@ -22,8 +22,4 @@ public class IllegalSetupProgressException extends OmnipodException {
return actual;
}
@Override
public boolean isCertainFailure() {
return true;
}
}

View file

@ -2,15 +2,10 @@ package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public class MessageDecodingException extends OmnipodException {
public MessageDecodingException(String message) {
super(message);
super(message, false);
}
public MessageDecodingException(String message, Throwable cause) {
super(message, cause);
}
@Override
public boolean isCertainFailure() {
return false;
super(message, cause, false);
}
}

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public class NonceOutOfSyncException extends OmnipodException {
public NonceOutOfSyncException() {
super("Nonce out of sync", true);
}
}

View file

@ -2,11 +2,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public class NonceResyncException extends OmnipodException {
public NonceResyncException() {
super("Nonce resync failed");
}
@Override
public boolean isCertainFailure() {
return true;
super("Nonce resync failed", true);
}
}

View file

@ -6,16 +6,11 @@ public class NotEnoughDataException extends OmnipodException {
private final byte[] data;
public NotEnoughDataException(byte[] data) {
super("Not enough data: " + ByteUtil.shortHexString(data));
super("Not enough data: " + ByteUtil.shortHexString(data), false);
this.data = data;
}
public byte[] getData() {
return data;
}
@Override
public boolean isCertainFailure() {
return false;
}
}

View file

@ -1,13 +1,23 @@
package info.nightscout.androidaps.plugins.pump.omnipod.exception;
public abstract class OmnipodException extends RuntimeException {
public OmnipodException(String message) {
private boolean certainFailure;
public OmnipodException(String message, boolean certainFailure) {
super(message);
this.certainFailure = certainFailure;
}
public OmnipodException(String message, Throwable cause) {
public OmnipodException(String message, Throwable cause, boolean certainFailure) {
super(message, cause);
this.certainFailure = certainFailure;
}
public abstract boolean isCertainFailure();
public boolean isCertainFailure() {
return certainFailure;
}
public void setCertainFailure(boolean certainFailure) {
this.certainFailure = certainFailure;
}
}

View file

@ -9,16 +9,11 @@ public class PodFaultException extends OmnipodException {
public PodFaultException(PodInfoFaultEvent faultEvent) {
super(String.format(Locale.getDefault(), "Pod fault (%d): %s", faultEvent.getFaultEventCode().getValue(),
faultEvent.getFaultEventCode().toString()));
faultEvent.getFaultEventCode().toString()), true);
this.faultEvent = faultEvent;
}
public PodInfoFaultEvent getFaultEvent() {
return faultEvent;
}
@Override
public boolean isCertainFailure() {
return true;
}
}

View file

@ -6,16 +6,11 @@ public class PodReturnedErrorResponseException extends OmnipodException {
private final ErrorResponse errorResponse;
public PodReturnedErrorResponseException(ErrorResponse errorResponse) {
super("Pod returned error response: " + errorResponse.getErrorResponseType());
super("Pod returned error response: " + errorResponse.getErrorResponseType(), true);
this.errorResponse = errorResponse;
}
public ErrorResponse getErrorResponse() {
return errorResponse;
}
@Override
public boolean isCertainFailure() {
return true;
}
}

View file

@ -123,7 +123,7 @@ public class RileyLinkOmnipodService extends RileyLinkService {
podState = gson.fromJson(storedPodState, PodSessionState.class);
OmnipodUtil.setPodSessionState(podState);
} catch (Exception ex) {
LOG.error("Could not deserialize Pod state: " + ex.getClass().getSimpleName() + ": " + ex.getMessage());
LOG.error("Could not deserialize Pod state", ex);
}
}
OmnipodCommunicationService omnipodCommunicationService = new OmnipodCommunicationService(rfspy);

View file

@ -1651,10 +1651,11 @@
<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_invalid_packet_type">Communication failed: received an invalid packet from the pod.</string>
<string name="omnipod_driver_error_invalid_progress_state">Communication failed: rhe pod is in a wrong state. Please retry your command.</string>
<string name="omnipod_driver_error_invalid_progress_state">Communication failed: the pod is in a wrong state.</string>
<string name="omnipod_driver_error_invalid_response">Communication failed: received an invalid response from the pod.</string>
<string name="omnipod_driver_error_message_decoding_failed">Communication failed: failed to decode message from the pod.</string>
<string name="omnipod_driver_error_nonce_resync_failed">Communication failed: nonce resync failed. Please try your command again.</string>
<string name="omnipod_driver_error_nonce_resync_failed">Communication failed: nonce resync failed.</string>
<string name="omnipod_driver_error_nonce_out_of_sync">Communication failed: nonce out of sync.</string>
<string name="omnipod_driver_error_not_enough_data">Communication failed: not enough data received from the pod.</string>
<string name="omnipod_driver_error_pod_fault">A pod fault (%1$s) has been detected. Please deactivate your pod and start a new one.</string>
<string name="omnipod_driver_error_pod_returned_error_response">Communication failed: the pod returned an error response.</string>