Merge pull request #45 from AAPS-Omnipod/omnipod_eros_dev_upstream_merge

Latest Omnipod updates
This commit is contained in:
Milos Kozak 2020-10-22 09:25:44 +02:00 committed by GitHub
commit 492bb349dd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 930 additions and 562 deletions

View file

@ -30,6 +30,7 @@ import info.nightscout.androidaps.plugins.profile.local.LocalProfileFragment
import info.nightscout.androidaps.plugins.profile.local.LocalProfilePlugin
import info.nightscout.androidaps.plugins.profile.ns.NSProfileFragment
import info.nightscout.androidaps.plugins.profile.ns.NSProfilePlugin
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin
import info.nightscout.androidaps.setupwizard.elements.*
import info.nightscout.androidaps.setupwizard.events.EventSWUpdate
@ -324,6 +325,12 @@ class SWDefinition @Inject constructor(
val activePump = activePlugin.activePump
activePump is OmnipodPumpPlugin && !activePump.isRileyLinkReady
}))
.add( // Omnipod only
SWEventListener(injector, EventRileyLinkDeviceStatusChange::class.java)
.label(R.string.setupwizard_pump_riley_link_status)
.visibility(SWValidator {
activePlugin.activePump is OmnipodPumpPlugin
}))
.add(SWButton(injector)
.text(R.string.readstatus)
.action(Runnable { commandQueue.readStatus("Clicked connect to pump", null) })
@ -332,7 +339,8 @@ class SWDefinition @Inject constructor(
// Getting the status might not be possible
activePlugin.activePump !is OmnipodPumpPlugin
}))
.add(SWEventListener(injector, EventPumpStatusChanged::class.java))
.add(SWEventListener(injector, EventPumpStatusChanged::class.java)
.visibility(SWValidator { activePlugin.activePump !is OmnipodPumpPlugin }))
.validator(SWValidator {
isPumpInitialized()
})

View file

@ -19,6 +19,7 @@ class SWEventListener constructor(
private var textLabel = 0
private var status = ""
private var textView: TextView? = null
private var visibilityValidator: SWValidator? = null
// TODO: Adrian how to clear disposable in this case?
init {
@ -43,6 +44,11 @@ class SWEventListener constructor(
return this
}
fun visibility(visibilityValidator: SWValidator): SWEventListener {
this.visibilityValidator = visibilityValidator
return this
}
@SuppressLint("SetTextI18n")
override fun generateDialog(layout: LinearLayout) {
val context = layout.context
@ -51,4 +57,8 @@ class SWEventListener constructor(
textView?.text = (if (textLabel != 0) resourceHelper.gs(textLabel) else "") + " " + status
layout.addView(textView)
}
override fun processVisibility() {
if (visibilityValidator != null && !visibilityValidator!!.isValid) textView?.visibility = View.GONE else textView?.visibility = View.VISIBLE
}
}

View file

@ -1446,4 +1446,5 @@
<string name="worker_state">Worker State: %s</string>
<string name="uploaded_data">Uploaded Data</string>
<string name="the_following_data_will_be_uploaded_to_your_open_humans_account">The following data will be uploaded to your Open Humans account: Glucose values, boluses, carbs, careportal events (except notes), extended boluses, profile switches, total daily doses, temporary basals, temp targets, preferences, application version, device model and screen dimensions. Secret or private information such as your Nightscout URL or API secret will not be uploaded.</string>
<string name="setupwizard_pump_riley_link_status">RileyLink status:</string>
</resources>

View file

@ -72,6 +72,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodCustomA
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
@ -254,6 +255,13 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
// When PodStateManager is created, which causes an IllegalArgumentException for DateTimeZones not being recognized
podStateManager.loadPodState();
// BS @ 2020-10-17 FIXME: for backwards compatibility; remove before release
if (podStateManager.isPodInitialized() &&
podStateManager.getActivationProgress() == ActivationProgress.NONE &&
podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.ABOVE_FIFTY_UNITS)) {
podStateManager.setActivationProgress(ActivationProgress.COMPLETED);
}
lastConnectionTimeMillis = sp.getLong(
RileyLinkConst.Prefs.LastGoodDeviceCommunicationTime, 0L);
@ -895,8 +903,14 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
@NonNull @Override public PumpEnactResult setTempBasalPercent(Integer percent, Integer
durationInMinutes, Profile profile, boolean enforceNew) {
aapsLogger.debug(LTag.PUMP, "setTempBasalPercent [OmnipodPumpPlugin] - Not implemented.");
return getOperationNotSupportedWithCustomText(info.nightscout.androidaps.core.R.string.pump_operation_not_supported_by_pump_driver);
if (percent == 0) {
return setTempBasalAbsolute(0.0d, durationInMinutes, profile, enforceNew);
} else {
double absoluteValue = profile.getBasal() * (percent / 100.0d);
absoluteValue = pumpDescription.pumpType.determineCorrectBasalSize(absoluteValue);
aapsLogger.warn(LTag.PUMP, "setTempBasalPercent [OmnipodPumpPlugin] - You are trying to use setTempBasalPercent with percent other then 0% (" + percent + "). This will start setTempBasalAbsolute, with calculated value (" + absoluteValue + "). Result might not be 100% correct.");
return setTempBasalAbsolute(absoluteValue, durationInMinutes, profile, enforceNew);
}
}
@NonNull @Override public PumpEnactResult setExtendedBolus(Double insulin, Integer
@ -916,7 +930,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
}
private void initializeAfterRileyLinkConnection() {
if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAtLeast(PodProgressStatus.PAIRING_COMPLETED)) {
if (podStateManager.getActivationProgress().isAtLeast(ActivationProgress.PAIRING_COMPLETED)) {
for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) {
PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
if (result.success) {

View file

@ -5,40 +5,45 @@ import org.joda.time.DateTimeZone;
import java.util.Collections;
import java.util.Random;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.OmnipodMessage;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.AssignAddressCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.VersionResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageAddressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalVersionResponseTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
public class AssignAddressAction implements OmnipodAction<VersionResponse> {
public class AssignAddressAction implements OmnipodAction<Void> {
private final PodStateManager podStateManager;
private final AAPSLogger aapsLogger;
public AssignAddressAction(PodStateManager podStateManager) {
public AssignAddressAction(PodStateManager podStateManager, AAPSLogger aapsLogger) {
if (podStateManager == null) {
throw new IllegalArgumentException("podStateManager can not be null");
}
if (aapsLogger == null) {
throw new IllegalArgumentException("Logger can not be null");
}
this.podStateManager = podStateManager;
this.aapsLogger = aapsLogger;
}
@Override
public VersionResponse execute(OmnipodRileyLinkCommunicationManager communicationService) {
public Void execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (!podStateManager.hasPodState()) {
podStateManager.initState(generateRandomAddress());
}
if (podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus().isAfter(PodProgressStatus.REMINDER_INITIALIZED)) {
throw new IllegalPodProgressException(PodProgressStatus.REMINDER_INITIALIZED, podStateManager.getPodProgressStatus());
}
if (podStateManager.getActivationProgress().needsPairing()) {
AssignAddressCommand assignAddress = new AssignAddressCommand(podStateManager.getAddress());
OmnipodMessage assignAddressMessage = new OmnipodMessage(OmnipodConstants.DEFAULT_ADDRESS,
Collections.singletonList(assignAddress), podStateManager.getMessageNumber());
try {
VersionResponse assignAddressResponse = communicationService.exchangeMessages(VersionResponse.class, podStateManager, assignAddressMessage,
OmnipodConstants.DEFAULT_ADDRESS, podStateManager.getAddress());
@ -51,8 +56,17 @@ public class AssignAddressAction implements OmnipodAction<VersionResponse> {
podStateManager.setInitializationParameters(assignAddressResponse.getLot(), assignAddressResponse.getTid(), //
assignAddressResponse.getPiVersion(), assignAddressResponse.getPmVersion(), DateTimeZone.getDefault(), assignAddressResponse.getPodProgressStatus());
} catch (IllegalPacketTypeException ex) {
if (ex.getActual() == PacketType.ACK && podStateManager.isPodInitialized()) {
// When we already assigned the address before, it's possible to only get an ACK here
aapsLogger.debug("Received ACK instead of response in AssignAddressAction. Ignoring because we already assigned the address successfully");
} else {
throw ex;
}
}
}
return assignAddressResponse;
return null;
}
private static int generateRandomAddress() {

View file

@ -6,16 +6,15 @@ import java.util.List;
import java.util.Optional;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.ExpirationReminderBuilder;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalActivationProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
public class InsertCannulaAction implements OmnipodAction<StatusResponse> {
public class InsertCannulaAction implements OmnipodAction<Void> {
private final PodStateManager podStateManager;
private final BasalSchedule initialBasalSchedule;
@ -37,31 +36,33 @@ public class InsertCannulaAction implements OmnipodAction<StatusResponse> {
}
@Override
public StatusResponse execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, podStateManager.isPodInitialized() ? podStateManager.getPodProgressStatus() : null);
public Void execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (podStateManager.getActivationProgress().isBefore(ActivationProgress.PRIMING_COMPLETED)) {
throw new IllegalActivationProgressException(ActivationProgress.PRIMING_COMPLETED, podStateManager.getActivationProgress());
}
if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.BASAL_INITIALIZED)) {
if (podStateManager.getActivationProgress().needsBasalSchedule()) {
podStateManager.setBasalSchedule(initialBasalSchedule);
communicationService.executeAction(new SetBasalScheduleAction(podStateManager, initialBasalSchedule,
true, podStateManager.getScheduleOffset(), false));
podStateManager.setActivationProgress(ActivationProgress.BASAL_INITIALIZED);
}
if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.INSERTING_CANNULA)) {
if (podStateManager.getActivationProgress().needsExpirationReminders()) {
communicationService.executeAction(new ConfigureAlertsAction(podStateManager, buildAlertConfigurations()));
podStateManager.setExpirationAlertTimeBeforeShutdown(expirationReminderTimeBeforeShutdown);
podStateManager.setLowReservoirAlertUnits(lowReservoirAlertUnits);
return communicationService.executeAction(new BolusAction(podStateManager, OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS,
Duration.standardSeconds(1), false, false));
} else if (podStateManager.getPodProgressStatus().equals(PodProgressStatus.INSERTING_CANNULA)) {
// Check status
return communicationService.executeAction(new GetStatusAction(podStateManager));
} else {
throw new IllegalPodProgressException(null, podStateManager.getPodProgressStatus());
podStateManager.setActivationProgress(ActivationProgress.EXPIRATION_REMINDERS_SET);
}
if (podStateManager.getActivationProgress().needsCannulaInsertion()) {
communicationService.executeAction(new BolusAction(podStateManager, OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS,
Duration.standardSeconds(1), false, false));
podStateManager.setActivationProgress(ActivationProgress.INSERTING_CANNULA);
}
return null;
}
private List<AlertConfiguration> buildAlertConfigurations() {

View file

@ -1,13 +1,12 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.action.service.PrimeService;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalActivationProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
public class PrimeAction implements OmnipodAction<StatusResponse> {
public class PrimeAction implements OmnipodAction<Void> {
private final PrimeService service;
private final PodStateManager podStateManager;
@ -24,21 +23,27 @@ public class PrimeAction implements OmnipodAction<StatusResponse> {
}
@Override
public StatusResponse execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PAIRING_COMPLETED)) {
throw new IllegalPodProgressException(PodProgressStatus.PAIRING_COMPLETED, podStateManager.isPodInitialized() ? podStateManager.getPodProgressStatus() : null);
public Void execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (podStateManager.getActivationProgress().isBefore(ActivationProgress.PAIRING_COMPLETED)) {
throw new IllegalActivationProgressException(ActivationProgress.PAIRING_COMPLETED, podStateManager.getActivationProgress());
}
if (podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING)) {
if (podStateManager.getActivationProgress().needsDisableTab5Sub16And17()) {
// FaultConfigCommand sets internal pod variables to effectively disable $6x faults which occur more often with a 0 TBR
service.executeDisableTab5Sub16And17FaultConfigCommand(communicationService, podStateManager);
podStateManager.setActivationProgress(ActivationProgress.TAB_5_SUB_16_AND_17_DISABLED);
}
if (podStateManager.getActivationProgress().needsSetupReminders()) {
service.executeFinishSetupReminderAlertCommand(communicationService, podStateManager);
return service.executePrimeBolusCommand(communicationService, podStateManager);
} else if (podStateManager.getPodProgressStatus().equals(PodProgressStatus.PRIMING)) {
// Check status
return communicationService.executeAction(new GetStatusAction(podStateManager));
} else {
throw new IllegalPodProgressException(null, podStateManager.getPodProgressStatus());
}
podStateManager.setActivationProgress(ActivationProgress.SETUP_REMINDERS_SET);
}
if (podStateManager.getActivationProgress().needsPriming()) {
service.executePrimeBolusCommand(communicationService, podStateManager);
podStateManager.setActivationProgress(ActivationProgress.PRIMING);
}
return null;
}
}

View file

@ -4,40 +4,52 @@ import org.joda.time.DateTime;
import java.util.Collections;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.OmnipodMessage;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.SetupPodCommand;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.VersionResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageAddressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalVersionResponseTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.manager.OmnipodRileyLinkCommunicationManager;
public class SetupPodAction implements OmnipodAction<VersionResponse> {
public class SetupPodAction implements OmnipodAction<Void> {
private final PodStateManager podStateManager;
private final AAPSLogger aapsLogger;
public SetupPodAction(PodStateManager podStateManager) {
public SetupPodAction(PodStateManager podStateManager, AAPSLogger aapsLogger) {
if (podStateManager == null) {
throw new IllegalArgumentException("Pod state manager can not be null");
}
if (aapsLogger == null) {
throw new IllegalArgumentException("Logger can not be null");
}
this.podStateManager = podStateManager;
this.aapsLogger = aapsLogger;
}
@Override
public VersionResponse execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (!podStateManager.isPodInitialized() || !podStateManager.getPodProgressStatus().equals(PodProgressStatus.REMINDER_INITIALIZED)) {
public Void execute(OmnipodRileyLinkCommunicationManager communicationService) {
if (!podStateManager.isPodInitialized()) {
throw new IllegalPodProgressException(PodProgressStatus.REMINDER_INITIALIZED, podStateManager.isPodInitialized() ? podStateManager.getPodProgressStatus() : null);
}
if (podStateManager.getActivationProgress().needsPairing()) {
DateTime activationDate = DateTime.now(podStateManager.getTimeZone());
SetupPodCommand setupPodCommand = new SetupPodCommand(podStateManager.getAddress(), activationDate,
podStateManager.getLot(), podStateManager.getTid());
OmnipodMessage message = new OmnipodMessage(OmnipodConstants.DEFAULT_ADDRESS,
Collections.singletonList(setupPodCommand), podStateManager.getMessageNumber());
VersionResponse setupPodResponse;
setupPodResponse = communicationService.exchangeMessages(VersionResponse.class, podStateManager,
try {
VersionResponse setupPodResponse = communicationService.exchangeMessages(VersionResponse.class, podStateManager,
message, OmnipodConstants.DEFAULT_ADDRESS, podStateManager.getAddress());
if (!setupPodResponse.isSetupPodVersionResponse()) {
@ -46,7 +58,18 @@ public class SetupPodAction implements OmnipodAction<VersionResponse> {
if (setupPodResponse.getAddress() != podStateManager.getAddress()) {
throw new IllegalMessageAddressException(podStateManager.getAddress(), setupPodResponse.getAddress());
}
} catch (IllegalPacketTypeException ex) {
if (PacketType.ACK.equals(ex.getActual())) {
// Pod is already configured
aapsLogger.debug("Received ACK instead of response in SetupPodAction. Ignoring");
} else {
throw ex;
}
}
return setupPodResponse;
podStateManager.setActivationProgress(ActivationProgress.PAIRING_COMPLETED);
}
return null;
}
}

View file

@ -27,7 +27,10 @@ public class TempBasalExtraCommand extends MessageBlock {
} else if (rate > OmnipodConstants.MAX_BASAL_RATE) {
throw new IllegalArgumentException("Rate exceeds max basal rate");
}
if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) {
if (duration.isShorterThan(Duration.ZERO) || duration.equals(Duration.ZERO)) {
throw new IllegalArgumentException("Duration should be > 0");
} else if (duration.isLongerThan(OmnipodConstants.MAX_TEMP_BASAL_DURATION)) {
throw new IllegalArgumentException("Duration exceeds max temp basal duration");
}

View file

@ -12,9 +12,11 @@ public class ErrorResponse extends MessageBlock {
private static final int MESSAGE_LENGTH = 5;
private final byte errorResponseCode;
private Integer nonceSearchKey; // only valid for BAD_NONCE
private FaultEventCode faultEventCode; // valid for all but BAD_NONCE
private PodProgressStatus podProgressStatus; // valid for all but BAD_NONCE
private final Integer nonceSearchKey; // only valid for BAD_NONCE
private final FaultEventCode faultEventCode; // valid for all but BAD_NONCE
private final PodProgressStatus podProgressStatus; // valid for all but BAD_NONCE
public ErrorResponse(byte[] encodedData) {
if (encodedData.length < MESSAGE_LENGTH) {
@ -24,11 +26,16 @@ public class ErrorResponse extends MessageBlock {
errorResponseCode = encodedData[2];
if (this.errorResponseCode == ERROR_RESPONSE_CODE_BAD_NONCE) {
if (errorResponseCode == ERROR_RESPONSE_CODE_BAD_NONCE) {
nonceSearchKey = ByteUtil.makeUnsignedShort(encodedData[3], encodedData[4]);
faultEventCode = null;
podProgressStatus = null;
} else {
faultEventCode = FaultEventCode.fromByte(encodedData[3]);
podProgressStatus = PodProgressStatus.fromByte(encodedData[4]);
nonceSearchKey = null;
}
}
@ -53,8 +60,7 @@ public class ErrorResponse extends MessageBlock {
return nonceSearchKey;
}
@Override
public String toString() {
@Override public String toString() {
return "ErrorResponse{" +
"errorResponseCode=" + errorResponseCode +
", nonceSearchKey=" + nonceSearchKey +

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mes
import org.joda.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
@ -54,7 +53,7 @@ public class PodInfoActiveAlerts extends PodInfo {
@Override
public String toString() {
return "PodInfoActiveAlerts{" +
"word278=" + Arrays.toString(word278) +
"word278=" + ByteUtil.shortHexString(word278) +
", alertActivations=" + alertActivations +
'}';
}

View file

@ -6,13 +6,13 @@ import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusUpdatableResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSet;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ErrorEventInfo;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.LogEventErrorCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableResponse {
public class PodInfoDetailedStatus extends PodInfo implements StatusUpdatableResponse {
private static final int MINIMUM_MESSAGE_LENGTH = 21;
private final PodProgressStatus podProgressStatus;
@ -27,14 +27,13 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
private final Duration timeActive;
private final AlertSet unacknowledgedAlerts;
private final boolean faultAccessingTables;
private final LogEventErrorCode logEventErrorType;
private final PodProgressStatus logEventErrorPodProgressStatus;
private final ErrorEventInfo errorEventInfo;
private final byte receiverLowGain;
private final byte radioRSSI;
private final PodProgressStatus podProgressStatusAtTimeOfFirstLoggedFaultEvent;
private final PodProgressStatus previousPodProgressStatus;
private final byte[] unknownValue;
public PodInfoFaultEvent(byte[] encodedData) {
public PodInfoDetailedStatus(byte[] encodedData) {
super(encodedData);
if (encodedData.length < MINIMUM_MESSAGE_LENGTH) {
@ -69,19 +68,33 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
unacknowledgedAlerts = new AlertSet(encodedData[15]);
faultAccessingTables = encodedData[16] == 0x02;
int i = ByteUtil.convertUnsignedByteToInt(encodedData[17]);
byte value = (byte) (i >>> 4);
logEventErrorType = LogEventErrorCode.fromByte(value);
logEventErrorPodProgressStatus = PodProgressStatus.fromByte((byte) (encodedData[17] & 0x0f));
byte rawErrorEventInfo = encodedData[17];
if (rawErrorEventInfo == 0x00) {
errorEventInfo = null;
} else {
errorEventInfo = ErrorEventInfo.fromByte(rawErrorEventInfo);
}
receiverLowGain = (byte) (ByteUtil.convertUnsignedByteToInt(encodedData[18]) >>> 6);
radioRSSI = (byte) (encodedData[18] & 0x3f);
podProgressStatusAtTimeOfFirstLoggedFaultEvent = PodProgressStatus.fromByte((byte) (encodedData[19] & 0x0f));
if (ByteUtil.convertUnsignedByteToInt(encodedData[19]) == 0xff) { // this byte is not valid (no fault has occurred)
previousPodProgressStatus = null;
} else {
previousPodProgressStatus = PodProgressStatus.fromByte((byte) (encodedData[19] & 0x0f));
}
unknownValue = ByteUtil.substring(encodedData, 20, 2);
}
@Override
public PodInfoType getType() {
return PodInfoType.FAULT_EVENT;
return PodInfoType.DETAILED_STATUS;
}
public boolean isFaulted() {
return faultEventCode != null;
}
public boolean isActivationTimeExceeded() {
return podProgressStatus == PodProgressStatus.ACTIVATION_TIME_EXCEEDED;
}
@Override public PodProgressStatus getPodProgressStatus() {
@ -132,12 +145,8 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
return faultAccessingTables;
}
public LogEventErrorCode getLogEventErrorType() {
return logEventErrorType;
}
public PodProgressStatus getLogEventErrorPodProgressStatus() {
return logEventErrorPodProgressStatus;
public ErrorEventInfo getErrorEventInfo() {
return errorEventInfo;
}
public byte getReceiverLowGain() {
@ -148,8 +157,8 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
return radioRSSI;
}
public PodProgressStatus getPodProgressStatusAtTimeOfFirstLoggedFaultEvent() {
return podProgressStatusAtTimeOfFirstLoggedFaultEvent;
public PodProgressStatus getPreviousPodProgressStatus() {
return previousPodProgressStatus;
}
public byte[] getUnknownValue() {
@ -157,7 +166,7 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
}
@Override public String toString() {
return "PodInfoFaultEvent{" +
return "PodInfoDetailedStatus{" +
"podProgressStatus=" + podProgressStatus +
", deliveryStatus=" + deliveryStatus +
", bolusNotDelivered=" + bolusNotDelivered +
@ -170,11 +179,10 @@ public class PodInfoFaultEvent extends PodInfo implements StatusUpdatableRespons
", timeActive=" + timeActive +
", unacknowledgedAlerts=" + unacknowledgedAlerts +
", faultAccessingTables=" + faultAccessingTables +
", logEventErrorType=" + logEventErrorType +
", logEventErrorPodProgressStatus=" + logEventErrorPodProgressStatus +
", errorEventInfo=" + errorEventInfo +
", receiverLowGain=" + receiverLowGain +
", radioRSSI=" + radioRSSI +
", podProgressStatusAtTimeOfFirstLoggedFaultEvent=" + podProgressStatusAtTimeOfFirstLoggedFaultEvent +
", previousPodProgressStatus=" + previousPodProgressStatus +
", unknownValue=" + ByteUtil.shortHexString(unknownValue) +
'}';
}

View file

@ -0,0 +1,66 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
public enum ActivationProgress {
NONE,
PAIRING_COMPLETED,
TAB_5_SUB_16_AND_17_DISABLED,
SETUP_REMINDERS_SET,
PRIMING,
PRIMING_COMPLETED,
BASAL_INITIALIZED,
EXPIRATION_REMINDERS_SET,
INSERTING_CANNULA,
COMPLETED;
public boolean needsPairing() {
return this == NONE;
}
public boolean needsDisableTab5Sub16And17() {
return this == PAIRING_COMPLETED;
}
public boolean needsSetupReminders() {
return this == TAB_5_SUB_16_AND_17_DISABLED;
}
public boolean needsPriming() {
return this == SETUP_REMINDERS_SET;
}
public boolean needsPrimingVerification() {
return this == PRIMING;
}
public boolean needsBasalSchedule() {
return this == PRIMING_COMPLETED;
}
public boolean needsExpirationReminders() {
return this == BASAL_INITIALIZED;
}
public boolean needsCannulaInsertion() {
return this == EXPIRATION_REMINDERS_SET;
}
public boolean needsCannulaInsertionVerification() {
return this == INSERTING_CANNULA;
}
public boolean isCompleted() {
return this == COMPLETED;
}
public boolean isBefore(ActivationProgress other) {
return ordinal() < other.ordinal();
}
public boolean isAtLeast(ActivationProgress other) {
return ordinal() >= other.ordinal();
}
public boolean isAfter(ActivationProgress other) {
return ordinal() > other.ordinal();
}
}

View file

@ -0,0 +1,52 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
public final class ErrorEventInfo {
private final boolean insulinStateTableCorruption;
private final byte internalVariable;
private final boolean immediateBolusInProgress;
private final PodProgressStatus podProgressStatus;
private ErrorEventInfo(boolean insulinStateTableCorruption, byte internalVariable, boolean immediateBolusInProgress, PodProgressStatus podProgressStatus) {
this.insulinStateTableCorruption = insulinStateTableCorruption;
this.internalVariable = internalVariable;
this.immediateBolusInProgress = immediateBolusInProgress;
this.podProgressStatus = podProgressStatus;
}
public static ErrorEventInfo fromByte(byte faultEventInfo) {
int loggedFaultEventInfo = ByteUtil.convertUnsignedByteToInt(faultEventInfo);
boolean insulinStateTableCorruption = (loggedFaultEventInfo & 0x80) == 0x80;
byte internalVariable = (byte) ((loggedFaultEventInfo >>> 5) & 0x03);
boolean immediateBolusInProgress = (loggedFaultEventInfo & 0x10) == 0x10;
PodProgressStatus podProgressStatus = PodProgressStatus.fromByte((byte) (loggedFaultEventInfo & 0x0f));
return new ErrorEventInfo(insulinStateTableCorruption, internalVariable, immediateBolusInProgress, podProgressStatus);
}
public boolean isInsulinStateTableCorruption() {
return insulinStateTableCorruption;
}
public byte getInternalVariable() {
return internalVariable;
}
public boolean isImmediateBolusInProgress() {
return immediateBolusInProgress;
}
public PodProgressStatus getPodProgressStatus() {
return podProgressStatus;
}
@Override public String toString() {
return "ErrorEventInfo{" +
"insulinStateTableCorruption=" + insulinStateTableCorruption +
", internalVariable=" + internalVariable +
", immediateBolusInProgress=" + immediateBolusInProgress +
", podProgressStatus=" + podProgressStatus +
'}';
}
}

View file

@ -3,7 +3,6 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
import java.util.Locale;
public enum FaultEventCode {
NO_FAULTS((byte) 0x00),
FAILED_FLASH_ERASE((byte) 0x01),
FAILED_FLASH_STORE((byte) 0x02),
TABLE_CORRUPTION_BASAL_SUBCOMMAND((byte) 0x03),
@ -129,6 +128,9 @@ public enum FaultEventCode {
}
public static FaultEventCode fromByte(byte value) {
if (value == 0x00) { // No faults
return null;
}
for (FaultEventCode type : values()) {
if (type.value == value) {
return type;

View file

@ -1,24 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
public enum LogEventErrorCode {
NONE((byte) 0x00),
IMMEDIATE_BOLUS_IN_PROGRESS((byte) 0x01),
INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2((byte) 0x02),
INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_3((byte) 0x03),
INSULIN_STATE_TABLE_CORRUPTION((byte) 0x04);
private final byte value;
LogEventErrorCode(byte value) {
this.value = value;
}
public static LogEventErrorCode fromByte(byte value) {
for (LogEventErrorCode type : values()) {
if (type.value == value) {
return type;
}
}
throw new IllegalArgumentException("Unknown LogEventErrorCode: " + value);
}
}

View file

@ -3,15 +3,15 @@ package info.nightscout.androidaps.plugins.pump.omnipod.driver.definition;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfo;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoActiveAlerts;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoDataLog;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoDetailedStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultAndInitializationTime;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultEvent;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoOlderPulseLog;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog;
public enum PodInfoType {
NORMAL((byte) 0x00),
ACTIVE_ALERTS((byte) 0x01),
FAULT_EVENT((byte) 0x02),
DETAILED_STATUS((byte) 0x02),
DATA_LOG((byte) 0x03), // Similar to types $50 & $51. Returns up to the last 60 dwords of data.
FAULT_AND_INITIALIZATION_TIME((byte) 0x05),
RECENT_PULSE_LOG((byte) 0x50), // Starting at $4200
@ -44,8 +44,8 @@ public enum PodInfoType {
throw new UnsupportedOperationException("Cannot decode PodInfoType.NORMAL");
case ACTIVE_ALERTS:
return new PodInfoActiveAlerts(encodedData);
case FAULT_EVENT:
return new PodInfoFaultEvent(encodedData);
case DETAILED_STATUS:
return new PodInfoDetailedStatus(encodedData);
case DATA_LOG:
return new PodInfoDataLog(encodedData, bodyLength);
case FAULT_AND_INITIALIZATION_TIME:

View file

@ -0,0 +1,7 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
public class ActivationTimeExceededException extends OmnipodException {
public ActivationTimeExceededException() {
super("The Pod's activation time has been exceeded", true);
}
}

View file

@ -0,0 +1,24 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
import java.util.Locale;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
public class IllegalActivationProgressException extends OmnipodException {
private final ActivationProgress expected;
private final ActivationProgress actual;
public IllegalActivationProgressException(ActivationProgress expected, ActivationProgress actual) {
super(String.format(Locale.getDefault(), "Illegal activation progress: %s, expected: %s", actual, expected), true);
this.expected = expected;
this.actual = actual;
}
public ActivationProgress getExpected() {
return expected;
}
public ActivationProgress getActual() {
return actual;
}
}

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), true);
super(String.format(Locale.getDefault(), "Illegal Pod progress: %s, expected: %s", actual, expected), true);
this.expected = expected;
this.actual = actual;
}

View file

@ -1,16 +1,16 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.exception;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultEvent;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoDetailedStatus;
public class PodFaultException extends OmnipodException {
private final PodInfoFaultEvent faultEvent;
private final PodInfoDetailedStatus detailedStatus;
public PodFaultException(PodInfoFaultEvent faultEvent) {
super(faultEvent.getFaultEventCode().toString(), true);
this.faultEvent = faultEvent;
public PodFaultException(PodInfoDetailedStatus detailedStatus) {
super(detailedStatus.getFaultEventCode().toString(), true);
this.detailedStatus = detailedStatus;
}
public PodInfoFaultEvent getFaultEvent() {
return faultEvent;
public PodInfoDetailedStatus getDetailedStatus() {
return detailedStatus;
}
}

View file

@ -31,19 +31,19 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoRecentPulseLog;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertConfiguration;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.BeepType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalActivationProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NonceOutOfSyncException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.OmnipodException;
@ -59,7 +59,7 @@ import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.SingleSubject;
public class OmnipodManager {
private static final int ACTION_VERIFICATION_TRIES = 2;
private static final int ACTION_VERIFICATION_TRIES = 1;
private final OmnipodRileyLinkCommunicationManager communicationService;
private PodStateManager podStateManager;
@ -88,76 +88,53 @@ public class OmnipodManager {
}
public synchronized Single<Boolean> pairAndPrime() {
logStartingCommandExecution("pairAndPrime");
if (podStateManager.isPodInitialized()) {
if (podStateManager.getActivationProgress().isAfter(ActivationProgress.PRIMING)) {
return Single.just(true);
}
if (podStateManager.getActivationProgress().needsPrimingVerification()) {
return Single.fromCallable(() -> verifyPodProgressStatus(PodProgressStatus.PRIMING_COMPLETED, ActivationProgress.PRIMING_COMPLETED));
}
}
try {
if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PAIRING_COMPLETED)) {
// Always send both 0x07 and 0x03 on retries
try {
if (podStateManager.getActivationProgress().isBefore(ActivationProgress.PAIRING_COMPLETED)) {
communicationService.executeAction(
new AssignAddressAction(podStateManager));
} catch (IllegalPacketTypeException ex) {
if (ex.getActual() == PacketType.ACK && podStateManager.isPodInitialized()) {
// When we already assigned the address before, it's possible to only get an ACK here
aapsLogger.debug("Received ACK instead of response in AssignAddressAction. Ignoring because we already assigned the address successfully");
} else {
throw ex;
}
}
new AssignAddressAction(podStateManager, aapsLogger));
try {
communicationService.executeAction(new SetupPodAction(podStateManager));
} catch (IllegalPacketTypeException ex) {
if (PacketType.ACK.equals(ex.getActual())) {
// TODO is this true for the SetupPodCommand?
// Pod is already configured
aapsLogger.debug("Received ACK instead of response in SetupPodAction. Ignoring");
communicationService.executeAction(new SetupPodAction(podStateManager, aapsLogger));
}
}
} else if (podStateManager.getPodProgressStatus().isAfter(PodProgressStatus.PRIMING)) {
throw new IllegalPodProgressException(PodProgressStatus.PAIRING_COMPLETED, podStateManager.getPodProgressStatus());
}
// Make sure we have an up to date PodProgressStatus
getPodStatus();
communicationService.executeAction(new PrimeAction(new PrimeService(), podStateManager));
} finally {
logCommandExecutionFinished("pairAndPrime");
}
long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_PRIME_BOLUS_UNITS, OmnipodConstants.POD_PRIMING_DELIVERY_RATE).getMillis();
return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) //
.map(o -> verifyPodProgressStatus(PodProgressStatus.PRIMING_COMPLETED)) //
.map(o -> verifyPodProgressStatus(PodProgressStatus.PRIMING_COMPLETED, ActivationProgress.PRIMING_COMPLETED)) //
.subscribeOn(Schedulers.io());
}
public synchronized Single<Boolean> insertCannula(
BasalSchedule basalSchedule, Duration expirationReminderTimeBeforeShutdown, Integer lowReservoirAlertUnits) {
if (!podStateManager.isPodInitialized() || podStateManager.getPodProgressStatus().isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, !podStateManager.isPodInitialized() ? null : podStateManager.getPodProgressStatus());
if (podStateManager.getActivationProgress().isBefore(ActivationProgress.PRIMING_COMPLETED)) {
throw new IllegalActivationProgressException(ActivationProgress.PRIMING_COMPLETED, podStateManager.getActivationProgress());
}
// Make sure we have the latest PodProgressStatus
getPodStatus();
if (podStateManager.getPodProgressStatus().isAfter(PodProgressStatus.INSERTING_CANNULA)) {
throw new IllegalPodProgressException(PodProgressStatus.PRIMING_COMPLETED, podStateManager.getPodProgressStatus());
if (podStateManager.isPodInitialized()) {
if (podStateManager.getActivationProgress().isCompleted()) {
return Single.just(true);
}
if (podStateManager.getActivationProgress().needsCannulaInsertionVerification()) {
return Single.fromCallable(() -> verifyPodProgressStatus(PodProgressStatus.ABOVE_FIFTY_UNITS, ActivationProgress.COMPLETED));
}
}
logStartingCommandExecution("insertCannula [basalSchedule=" + basalSchedule + "]");
try {
communicationService.executeAction(new InsertCannulaAction(podStateManager, basalSchedule, expirationReminderTimeBeforeShutdown, lowReservoirAlertUnits));
} finally {
logCommandExecutionFinished("insertCannula");
}
long delayInMillis = calculateEstimatedBolusDuration(DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION), OmnipodConstants.POD_CANNULA_INSERTION_BOLUS_UNITS, OmnipodConstants.POD_CANNULA_INSERTION_DELIVERY_RATE).getMillis();
return Single.timer(delayInMillis, TimeUnit.MILLISECONDS) //
.map(o -> verifyPodProgressStatus(PodProgressStatus.ABOVE_FIFTY_UNITS)) //
.map(o -> verifyPodProgressStatus(PodProgressStatus.ABOVE_FIFTY_UNITS, ActivationProgress.COMPLETED)) //
.subscribeOn(Schedulers.io());
}
@ -166,49 +143,29 @@ public class OmnipodManager {
throw new IllegalPodProgressException(PodProgressStatus.REMINDER_INITIALIZED, null);
}
logStartingCommandExecution("getPodStatus");
try {
return communicationService.executeAction(new GetStatusAction(podStateManager));
} finally {
logCommandExecutionFinished("getPodStatus");
}
}
public synchronized PodInfoResponse getPodInfo(PodInfoType podInfoType) {
assertReadyForDelivery();
logStartingCommandExecution("getPodInfo");
try {
return communicationService.executeAction(new GetPodInfoAction(podStateManager, podInfoType));
} finally {
logCommandExecutionFinished("getPodInfo");
}
}
public synchronized StatusResponse configureAlerts(List<AlertConfiguration> alertConfigurations) {
assertReadyForDelivery();
logStartingCommandExecution("configureAlerts");
try {
StatusResponse statusResponse = executeAndVerify(() -> communicationService.executeAction(new ConfigureAlertsAction(podStateManager, alertConfigurations)));
ConfigureAlertsAction.updateConfiguredAlerts(podStateManager, alertConfigurations);
return statusResponse;
} finally {
logCommandExecutionFinished("configureAlerts");
}
}
public synchronized StatusResponse acknowledgeAlerts() {
assertReadyForDelivery();
logStartingCommandExecution("acknowledgeAlerts");
try {
return executeAndVerify(() -> communicationService.executeAction(new AcknowledgeAlertsAction(podStateManager, podStateManager.getActiveAlerts())));
} finally {
logCommandExecutionFinished("acknowledgeAlerts");
}
}
// CAUTION: cancels all delivery
@ -216,9 +173,6 @@ public class OmnipodManager {
public synchronized void setBasalSchedule(BasalSchedule schedule, boolean acknowledgementBeep) {
assertReadyForDelivery();
logStartingCommandExecution("setBasalSchedule [basalSchedule=" + schedule + ", acknowledgementBeep=" + acknowledgementBeep + "]");
try {
boolean wasSuspended = podStateManager.isSuspended();
if (!wasSuspended) {
suspendDelivery(acknowledgementBeep);
@ -247,22 +201,14 @@ public class OmnipodManager {
throw ex;
}
}
} finally {
logCommandExecutionFinished("setBasalSchedule");
}
}
// CAUTION: cancels temp basal and then sets new temp basal. An OmnipodException[certainFailure=false] indicates that the pod might have cancelled the previous temp basal, but did not set a new temp basal
public synchronized void setTemporaryBasal(double rate, Duration duration, boolean acknowledgementBeep, boolean completionBeep) {
assertReadyForDelivery();
logStartingCommandExecution("setTemporaryBasal [rate=" + rate + ", duration=" + duration + ", acknowledgementBeep=" + acknowledgementBeep + ", completionBeep=" + completionBeep + "]");
boolean cancelCurrentTbr = podStateManager.isTempBasalRunning();
try {
if (cancelCurrentTbr) {
try {
cancelDelivery(EnumSet.of(DeliveryType.TEMP_BASAL), acknowledgementBeep);
@ -320,9 +266,6 @@ public class OmnipodManager {
throw ex2;
}
}
} finally {
logCommandExecutionFinished("setTemporaryBasal");
}
}
public synchronized void cancelTemporaryBasal(boolean acknowledgementBeep) {
@ -348,17 +291,11 @@ public class OmnipodManager {
private synchronized StatusResponse cancelDelivery(EnumSet<DeliveryType> deliveryTypes, boolean acknowledgementBeep) {
assertReadyForDelivery();
logStartingCommandExecution("cancelDelivery [deliveryTypes=" + deliveryTypes + ", acknowledgementBeep=" + acknowledgementBeep + "]");
try {
return executeAndVerify(() -> {
StatusResponse statusResponse = communicationService.executeAction(new CancelDeliveryAction(podStateManager, deliveryTypes, acknowledgementBeep));
aapsLogger.info(LTag.PUMPCOMM, "Status response after cancel delivery[types={}]: {}", deliveryTypes.toString(), statusResponse.toString());
return statusResponse;
});
} finally {
logCommandExecutionFinished("cancelDelivery");
}
}
// Returns a SingleSubject that returns when the bolus has finished.
@ -367,8 +304,6 @@ public class OmnipodManager {
public synchronized BolusCommandResult bolus(Double units, boolean acknowledgementBeep, boolean completionBeep, BiConsumer<Double, Integer> progressIndicationConsumer) {
assertReadyForDelivery();
logStartingCommandExecution("bolus [units=" + units + ", acknowledgementBeep=" + acknowledgementBeep + ", completionBeep=" + completionBeep + "]");
bolusCommandExecutionSubject = SingleSubject.create();
CommandDeliveryStatus commandDeliveryStatus = CommandDeliveryStatus.SUCCESS;
@ -437,7 +372,7 @@ public class OmnipodManager {
break;
} catch (PodFaultException ex) {
// Subtract units not delivered in case of a Pod failure
bolusNotDelivered = ex.getFaultEvent().getBolusNotDelivered();
bolusNotDelivered = ex.getDetailedStatus().getBolusNotDelivered();
aapsLogger.debug(LTag.PUMPCOMM, "Caught PodFaultException in bolus completion verification", ex);
break;
@ -454,8 +389,6 @@ public class OmnipodManager {
})
.subscribe());
logCommandExecutionFinished("bolus");
return new BolusCommandResult(commandDeliveryStatus, bolusCompletionSubject);
}
@ -467,16 +400,12 @@ public class OmnipodManager {
throw new IllegalDeliveryStatusException(DeliveryStatus.BOLUS_IN_PROGRESS, podStateManager.getLastDeliveryStatus());
}
logStartingCommandExecution("cancelBolus [acknowledgementBeep=" + acknowledgementBeep + "]");
try {
StatusResponse statusResponse = cancelDelivery(EnumSet.of(DeliveryType.BOLUS), acknowledgementBeep);
discardActiveBolusData(statusResponse.getBolusNotDelivered());
} catch (PodFaultException ex) {
discardActiveBolusData(ex.getFaultEvent().getBolusNotDelivered());
discardActiveBolusData(ex.getDetailedStatus().getBolusNotDelivered());
throw ex;
} finally {
logCommandExecutionFinished("cancelBolus");
}
}
}
@ -494,7 +423,6 @@ public class OmnipodManager {
public synchronized void suspendDelivery(boolean acknowledgementBeep) {
assertReadyForDelivery();
logStartingCommandExecution("suspendDelivery");
try {
cancelDelivery(EnumSet.allOf(DeliveryType.class), acknowledgementBeep);
@ -508,8 +436,6 @@ public class OmnipodManager {
ex.setCertainFailure(true);
throw ex;
}
} finally {
logCommandExecutionFinished("suspendDelivery");
}
}
@ -518,9 +444,6 @@ public class OmnipodManager {
public synchronized void setTime(boolean acknowledgementBeeps) {
assertReadyForDelivery();
logStartingCommandExecution("setTime [acknowledgementBeeps=" + acknowledgementBeeps + "]");
try {
DateTimeZone oldTimeZone = podStateManager.getTimeZone();
try {
@ -535,9 +458,6 @@ public class OmnipodManager {
}
podStateManager.updateActivatedAt();
} finally {
logCommandExecutionFinished("setTime");
}
}
public synchronized void deactivatePod() {
@ -545,8 +465,6 @@ public class OmnipodManager {
throw new IllegalPodProgressException(PodProgressStatus.REMINDER_INITIALIZED, null);
}
logStartingCommandExecution("deactivatePod");
// Try to get pulse log for diagnostics
try {
PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG));
@ -561,8 +479,6 @@ public class OmnipodManager {
communicationService.executeAction(new DeactivatePodAction(podStateManager, true));
} catch (PodFaultException ex) {
aapsLogger.info(LTag.PUMPCOMM, "Ignoring PodFaultException in deactivatePod", ex);
} finally {
logCommandExecutionFinished("deactivatePod");
}
podStateManager.discardState();
@ -592,7 +508,6 @@ public class OmnipodManager {
// Only works for commands with nonce resyncable message blocks
private StatusResponse executeAndVerify(Supplier<StatusResponse> supplier) {
logStartingCommandExecution("verifyCommand");
try {
return supplier.get();
} catch (OmnipodException originalException) {
@ -616,8 +531,6 @@ public class OmnipodManager {
throw originalException;
}
}
} finally {
logCommandExecutionFinished("verifyCommand");
}
}
@ -632,7 +545,7 @@ public class OmnipodManager {
* @return true if the Pod's progress status matches the expected status, otherwise false
* @throws PodProgressStatusVerificationFailedException in case reading the Pod status fails
*/
private boolean verifyPodProgressStatus(PodProgressStatus expectedPodProgressStatus) {
private boolean verifyPodProgressStatus(PodProgressStatus expectedPodProgressStatus, ActivationProgress activationProgress) {
Boolean result = null;
Throwable lastException = null;
@ -641,6 +554,7 @@ public class OmnipodManager {
StatusResponse statusResponse = getPodStatus();
if (statusResponse.getPodProgressStatus().equals(expectedPodProgressStatus)) {
podStateManager.setActivationProgress(activationProgress);
return true;
} else {
result = false;
@ -681,14 +595,6 @@ public class OmnipodManager {
throw new DeliveryStatusVerificationFailedException(expectedStatus, verificationCause);
}
private void logStartingCommandExecution(String action) {
aapsLogger.debug(LTag.PUMPCOMM, "Starting command execution for action: " + action);
}
private void logCommandExecutionFinished(String action) {
aapsLogger.debug(LTag.PUMPCOMM, "Command execution finished for action: " + action);
}
private Duration calculateEstimatedBolusDuration(DateTime startTime, double units, double deliveryRateInUnitsPerSecond) {
if (!podStateManager.isPodActivationCompleted()) {
// No basal or temp basal is active yet

View file

@ -16,16 +16,19 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusUpdatableResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultEvent;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoDetailedStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSet;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertSlot;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.AlertType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FirmwareVersion;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodCrc;
@ -79,7 +82,7 @@ public abstract class PodStateManager {
* @return true if we have a Pod state and the Pod activation has been completed. The pod could also be dead at this point
*/
public final boolean isPodActivationCompleted() {
return isPodInitialized() && podState.getPodProgressStatus().isAtLeast(PodProgressStatus.ABOVE_FIFTY_UNITS) && podState.getPodProgressStatus() != PodProgressStatus.ACTIVATION_TIME_EXCEEDED;
return getActivationProgress().isCompleted();
}
/**
@ -97,6 +100,13 @@ public abstract class PodStateManager {
return isPodInitialized() && podState.getPodProgressStatus().equals(PodProgressStatus.FAULT_EVENT_OCCURRED);
}
/**
* @return true if the Pod's activation time has been exceeded
*/
public boolean isPodActivationTimeExceeded() {
return isPodInitialized() && getPodProgressStatus() == PodProgressStatus.ACTIVATION_TIME_EXCEEDED;
}
/**
* @return true if we have a Pod state and the Pod is dead, meaning it is either in a fault state or activation time has been exceeded or it is deactivated
*/
@ -232,16 +242,12 @@ public abstract class PodStateManager {
* a fault event, this does NOT necessarily mean that the Pod is not faulted. For a reliable
* indication on whether or not the pod is faulted, see {@link #isPodFaulted() isPodFaulted()}
*/
public final boolean hasFaultEvent() {
return podState != null && podState.getFaultEvent() != null;
public final boolean isFaulted() {
return podState != null && podState.getFaultEventCode() != null;
}
public final PodInfoFaultEvent getFaultEvent() {
return getSafe(() -> podState.getFaultEvent());
}
public final void setFaultEvent(PodInfoFaultEvent faultEvent) {
setAndStore(() -> podState.setFaultEvent(faultEvent));
public final FaultEventCode getFaultEventCode() {
return getSafe(() -> podState.getFaultEventCode());
}
public final AlertType getConfiguredAlertType(AlertSlot alertSlot) {
@ -331,6 +337,17 @@ public abstract class PodStateManager {
return activatedAt == null ? null : activatedAt.withZone(getSafe(() -> podState.getTimeZone())).plus(OmnipodConstants.NOMINAL_POD_LIFE);
}
public final ActivationProgress getActivationProgress() {
if (hasPodState()) {
return Optional.ofNullable(podState.getActivationProgress()).orElse(ActivationProgress.NONE);
}
return ActivationProgress.NONE;
}
public final void setActivationProgress(ActivationProgress activationProgress) {
setAndStore(() -> podState.setActivationProgress(activationProgress));
}
public final PodProgressStatus getPodProgressStatus() {
return getSafe(() -> podState.getPodProgressStatus());
}
@ -513,20 +530,20 @@ public abstract class PodStateManager {
/**
* Does not automatically store pod state in order to decrease I/O load
*/
public final void updateFromResponse(StatusUpdatableResponse statusResponse) {
public final void updateFromResponse(StatusUpdatableResponse status) {
setSafe(() -> {
if (podState.getActivatedAt() == null) {
DateTime activatedAtCalculated = DateTime.now().withZone(podState.getTimeZone()).minus(statusResponse.getTimeActive());
DateTime activatedAtCalculated = DateTime.now().withZone(podState.getTimeZone()).minus(status.getTimeActive());
podState.setActivatedAt(activatedAtCalculated);
}
podState.setSuspended(statusResponse.getDeliveryStatus() == DeliveryStatus.SUSPENDED);
podState.setActiveAlerts(statusResponse.getUnacknowledgedAlerts());
podState.setLastDeliveryStatus(statusResponse.getDeliveryStatus());
podState.setReservoirLevel(statusResponse.getReservoirLevel());
podState.setTotalTicksDelivered(statusResponse.getTicksDelivered());
podState.setPodProgressStatus(statusResponse.getPodProgressStatus());
podState.setTimeActive(statusResponse.getTimeActive());
if (statusResponse.getDeliveryStatus().isTbrRunning()) {
podState.setSuspended(status.getDeliveryStatus() == DeliveryStatus.SUSPENDED);
podState.setActiveAlerts(status.getUnacknowledgedAlerts());
podState.setLastDeliveryStatus(status.getDeliveryStatus());
podState.setReservoirLevel(status.getReservoirLevel());
podState.setTotalTicksDelivered(status.getTicksDelivered());
podState.setPodProgressStatus(status.getPodProgressStatus());
podState.setTimeActive(status.getTimeActive());
if (status.getDeliveryStatus().isTbrRunning()) {
if (!isTempBasalCertain() && isTempBasalRunning()) {
podState.setTempBasalCertain(true);
}
@ -535,6 +552,13 @@ public abstract class PodStateManager {
setTempBasal(null, null, null, true, false);
}
podState.setLastUpdatedFromResponse(DateTime.now());
if (status instanceof PodInfoDetailedStatus) {
PodInfoDetailedStatus detailedStatus = (PodInfoDetailedStatus) status;
if (detailedStatus.isFaulted()) {
podState.setFaultEventCode(detailedStatus.getFaultEventCode());
}
}
});
}
@ -626,11 +650,12 @@ public abstract class PodStateManager {
private DateTimeZone timeZone;
private DateTime activatedAt;
private Duration timeActive;
private PodInfoFaultEvent faultEvent;
private FaultEventCode faultEventCode;
private Double reservoirLevel;
private Integer totalTicksDelivered;
private boolean suspended;
private NonceState nonceState;
private ActivationProgress activationProgress = ActivationProgress.NONE;
private PodProgressStatus podProgressStatus;
private DeliveryStatus lastDeliveryStatus;
private AlertSet activeAlerts;
@ -751,12 +776,12 @@ public abstract class PodStateManager {
this.timeActive = timeActive;
}
PodInfoFaultEvent getFaultEvent() {
return faultEvent;
FaultEventCode getFaultEventCode() {
return faultEventCode;
}
void setFaultEvent(PodInfoFaultEvent faultEvent) {
this.faultEvent = faultEvent;
void setFaultEventCode(FaultEventCode faultEventCode) {
this.faultEventCode = faultEventCode;
}
Double getReservoirLevel() {
@ -799,6 +824,14 @@ public abstract class PodStateManager {
this.nonceState = nonceState;
}
ActivationProgress getActivationProgress() {
return activationProgress;
}
void setActivationProgress(ActivationProgress activationProgress) {
this.activationProgress = activationProgress;
}
PodProgressStatus getPodProgressStatus() {
return podProgressStatus;
}
@ -930,11 +963,12 @@ public abstract class PodStateManager {
", timeZone=" + timeZone +
", activatedAt=" + activatedAt +
", timeActive=" + timeActive +
", faultEvent=" + faultEvent +
", faultEventCode=" + faultEventCode +
", reservoirLevel=" + reservoirLevel +
", totalTicksDelivered=" + totalTicksDelivered +
", suspended=" + suspended +
", nonceState=" + nonceState +
", activationProgress=" + activationProgress +
", podProgressStatus=" + podProgressStatus +
", lastDeliveryStatus=" + lastDeliveryStatus +
", activeAlerts=" + activeAlerts +

View file

@ -50,9 +50,9 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.Deliver
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalSchedule;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.schedule.BasalScheduleEntry;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActivationTimeExceededException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CommandFailedAfterChangingDeliveryStatusException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.CrcMismatchException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.DeliveryStatusVerificationFailedException;
@ -62,6 +62,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalM
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPodProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalResponseException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalActivationProgressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalVersionResponseTypeException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.MessageDecodingException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.NonceOutOfSyncException;
@ -481,7 +482,10 @@ public class AapsOmnipodManager {
addTempBasalTreatment(System.currentTimeMillis(), pumpId, tempBasalPair);
return new PumpEnactResult(injector).success(true).enacted(true);
return new PumpEnactResult(injector)
.duration(tempBasalPair.getDurationMinutes())
.absolute(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()))
.success(true).enacted(true);
}
public PumpEnactResult cancelTemporaryBasal() {
@ -755,11 +759,9 @@ public class AapsOmnipodManager {
if (ex instanceof OmnipodException) {
aapsLogger.error(LTag.PUMP, String.format("Caught OmnipodException[certainFailure=%s] from OmnipodManager", ((OmnipodException) ex).isCertainFailure()), ex);
if (ex instanceof PodFaultException) {
FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode();
if (!(faultEventCode == FaultEventCode.NO_FAULTS && podStateManager.isPodInitialized() && podStateManager.getPodProgressStatus() == PodProgressStatus.ACTIVATION_TIME_EXCEEDED)) {
FaultEventCode faultEventCode = ((PodFaultException) ex).getDetailedStatus().getFaultEventCode();
showPodFaultNotification(faultEventCode);
}
}
} else {
aapsLogger.error(LTag.PUMP, "Caught an unexpected non-OmnipodException from OmnipodManager", ex);
}
@ -772,7 +774,8 @@ public class AapsOmnipodManager {
comment = getStringResource(R.string.omnipod_error_crc_mismatch);
} else if (ex instanceof IllegalPacketTypeException) {
comment = getStringResource(R.string.omnipod_error_invalid_packet_type);
} else if (ex instanceof IllegalPodProgressException || ex instanceof IllegalDeliveryStatusException) {
} else if (ex instanceof IllegalPodProgressException || ex instanceof IllegalActivationProgressException ||
ex instanceof IllegalDeliveryStatusException) {
comment = getStringResource(R.string.omnipod_error_invalid_progress_state);
} else if (ex instanceof IllegalVersionResponseTypeException) {
comment = getStringResource(R.string.omnipod_error_invalid_response);
@ -791,8 +794,10 @@ public class AapsOmnipodManager {
} else if (ex instanceof NotEnoughDataException) {
comment = getStringResource(R.string.omnipod_error_not_enough_data);
} else if (ex instanceof PodFaultException) {
FaultEventCode faultEventCode = ((PodFaultException) ex).getFaultEvent().getFaultEventCode();
FaultEventCode faultEventCode = ((PodFaultException) ex).getDetailedStatus().getFaultEventCode();
comment = createPodFaultErrorMessage(faultEventCode);
} else if (ex instanceof ActivationTimeExceededException) {
comment = getStringResource(R.string.omnipod_error_pod_fault_activation_time_exceeded);
} else if (ex instanceof PodReturnedErrorResponseException) {
comment = getStringResource(R.string.omnipod_error_pod_returned_error_response);
} else if (ex instanceof RileyLinkUnreachableException) {
@ -813,9 +818,6 @@ public class AapsOmnipodManager {
}
private String createPodFaultErrorMessage(FaultEventCode faultEventCode) {
if (faultEventCode == FaultEventCode.NO_FAULTS && podStateManager.getPodProgressStatus() == PodProgressStatus.ACTIVATION_TIME_EXCEEDED) {
return getStringResource(R.string.omnipod_error_pod_fault_activation_time_exceeded);
}
return getStringResource(R.string.omnipod_error_pod_fault,
ByteUtil.convertUnsignedByteToInt(faultEventCode.getValue()), faultEventCode.name());
}

View file

@ -23,12 +23,13 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.ErrorResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.StatusUpdatableResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfo;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoFaultEvent;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoDetailedStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo.PodInfoResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PacketType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.ActivationTimeExceededException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageAddressException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalMessageSequenceNumberException;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.exception.IllegalPacketTypeException;
@ -156,12 +157,21 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
podStateManager.setLastFailedCommunication(DateTime.now());
throw new PodReturnedErrorResponseException(error);
}
} else if (responseMessageBlock.getType() == MessageBlockType.POD_INFO_RESPONSE && ((PodInfoResponse) responseMessageBlock).getSubType() == PodInfoType.FAULT_EVENT) {
PodInfoFaultEvent faultEvent = (PodInfoFaultEvent) ((PodInfoResponse) responseMessageBlock).getPodInfo();
podStateManager.setFaultEvent(faultEvent);
// Treat as successful communication as the user will get notified and can work with this response
} else if (responseMessageBlock.getType() == MessageBlockType.POD_INFO_RESPONSE && ((PodInfoResponse) responseMessageBlock).getSubType() == PodInfoType.DETAILED_STATUS) {
PodInfoDetailedStatus detailedStatus = (PodInfoDetailedStatus) ((PodInfoResponse) responseMessageBlock).getPodInfo();
if (detailedStatus.isFaulted()) {
// Treat as successful communication in order to prevent false positive pump unreachable alarms
podStateManager.setLastSuccessfulCommunication(DateTime.now());
throw new PodFaultException(faultEvent);
throw new PodFaultException(detailedStatus);
} else if (detailedStatus.isActivationTimeExceeded()) {
// Treat as successful communication in order to prevent false positive pump unreachable alarms
podStateManager.setLastSuccessfulCommunication(DateTime.now());
throw new ActivationTimeExceededException();
} else {
// Shouldn't happen
podStateManager.setLastFailedCommunication(DateTime.now());
throw new IllegalResponseException(responseClass.getSimpleName(), responseMessageBlock.getType());
}
} else {
podStateManager.setLastFailedCommunication(DateTime.now());
throw new IllegalResponseException(responseClass.getSimpleName(), responseMessageBlock.getType());
@ -174,6 +184,7 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
} finally {
podStateManager.storePodState();
}
}
private MessageBlock transportMessages(PodStateManager podStateManager, OmnipodMessage message, Integer addressOverride, Integer ackAddressOverride) {

View file

@ -23,6 +23,7 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.dialog.RileyL
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
import info.nightscout.androidaps.plugins.pump.omnipod.OmnipodPumpPlugin
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.OmnipodConstants
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
@ -272,8 +273,8 @@ class OmnipodOverviewFragment : DaggerFragment() {
})
}
if (podStateManager.hasFaultEvent()) {
val faultEventCode = podStateManager.faultEvent.faultEventCode
if (podStateManager.isFaulted) {
val faultEventCode = podStateManager.faultEventCode
errors.add(resourceHelper.gs(R.string.omnipod_pod_status_pod_fault_description, faultEventCode.value, faultEventCode.name))
}
@ -345,9 +346,7 @@ class OmnipodOverviewFragment : DaggerFragment() {
if (!podStateManager.isPodInitialized) {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_activation)
} else {
if (PodProgressStatus.ACTIVATION_TIME_EXCEEDED == podStateManager.podProgressStatus) {
resourceHelper.gs(R.string.omnipod_pod_status_activation_time_exceeded)
} else if (podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING_COMPLETED)) {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_activation)
} else {
resourceHelper.gs(R.string.omnipod_pod_status_waiting_for_cannula_insertion)
@ -454,7 +453,7 @@ class OmnipodOverviewFragment : DaggerFragment() {
}
private fun updateRefreshStatusButton() {
omnipod_overview_button_refresh_status.isEnabled = podStateManager.isPodInitialized && podStateManager.podProgressStatus.isAtLeast(PodProgressStatus.PAIRING_COMPLETED)
omnipod_overview_button_refresh_status.isEnabled = podStateManager.isPodInitialized && podStateManager.activationProgress.isAtLeast(ActivationProgress.PAIRING_COMPLETED)
&& rileyLinkServiceData.rileyLinkServiceState.isReady && isQueueEmpty()
}

View file

@ -9,6 +9,7 @@ import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.pump.common.events.EventRileyLinkDeviceStatusChange
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.RileyLinkServiceData
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager
@ -94,7 +95,7 @@ class PodManagementActivity : NoSplashAppCompatActivity() {
if (rileyLinkServiceData.rileyLinkServiceState.isReady) {
omnipod_pod_management_button_activate_pod.isEnabled = !podStateManager.isPodActivationCompleted
omnipod_pod_management_button_deactivate_pod.isEnabled = podStateManager.isPodInitialized
omnipod_pod_management_button_deactivate_pod.isEnabled = podStateManager.activationProgress.isAtLeast(ActivationProgress.PAIRING_COMPLETED)
if (discardButtonEnabled) {
omnipod_pod_management_button_discard_pod.isEnabled = true
}

View file

@ -3,7 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation
import android.os.Bundle
import androidx.annotation.IdRes
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.activity.OmnipodWizardActivityBase
import javax.inject.Inject
@ -25,7 +25,7 @@ class PodActivationWizardActivity : OmnipodWizardActivityBase() {
setContentView(R.layout.omnipod_pod_activation_wizard_activity)
startDestination = savedInstanceState?.getInt(KEY_START_DESTINATION, R.id.fillPodInfoFragment)
?: if (!podStateManager.isPodInitialized || podStateManager.podProgressStatus.isBefore(PodProgressStatus.PRIMING_COMPLETED)) {
?: if (podStateManager.activationProgress.isBefore(ActivationProgress.PRIMING_COMPLETED)) {
R.id.fillPodInfoFragment
} else {
R.id.attachPodInfoFragment

View file

@ -3,8 +3,7 @@ package info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.activation.fra
import android.content.Intent
import android.os.Bundle
import android.view.View
import info.nightscout.androidaps.plugins.pump.omnipod.R
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ActivationProgress
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.common.fragment.ActionFragmentBase
import info.nightscout.androidaps.plugins.pump.omnipod.ui.wizard.deactivation.PodDeactivationWizardActivity
@ -27,8 +26,7 @@ abstract class PodActivationActionFragmentBase : ActionFragmentBase() {
}
override fun onActionFailure() {
if (podStateManager.isPodInitialized && podStateManager.podProgressStatus == PodProgressStatus.ACTIVATION_TIME_EXCEEDED) {
omnipod_wizard_action_error.setText(R.string.omnipod_error_pod_fault_activation_time_exceeded)
if (podStateManager.isPodActivationTimeExceeded && podStateManager.activationProgress.isAtLeast(ActivationProgress.PAIRING_COMPLETED)) {
omnipod_wizard_button_retry.visibility = View.GONE
omnipod_wizard_button_deactivate_pod.visibility = View.VISIBLE
}

View file

@ -93,7 +93,7 @@
<string name="omnipod_error_nonce_resync_failed">Communication failed: nonce resync failed</string>
<string name="omnipod_error_nonce_out_of_sync">Communication failed: nonce out of sync</string>
<string name="omnipod_error_not_enough_data">Communication failed: not enough data received from the Pod</string>
<string name="omnipod_error_pod_fault">A Pod fault (%1$03d %2$s) has been detected. Please deactivate your Pod and start a new one</string>
<string name="omnipod_error_pod_fault">A Pod fault (%1$03d %2$s) has been detected. Please deactivate your Pod and activate a new one</string>
<string name="omnipod_error_pod_returned_error_response">Communication failed: the Pod returned an error response</string>
<string name="omnipod_error_communication_failed_no_response_from_riley_link">No response from RileyLink</string>
<string name="omnipod_error_communication_failed_riley_link_interrupted">RileyLink interrupted</string>

View file

@ -0,0 +1,142 @@
package info.nightscout.androidaps.plugins.pump.omnipod;
import android.os.Looper;
import org.joda.time.DateTimeZone;
import org.joda.time.tz.UTCProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.ArrayList;
import dagger.android.AndroidInjector;
import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.CommandQueueProvider;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.AAPSLoggerTest;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.pump.common.data.TempBasalPair;
import info.nightscout.androidaps.plugins.pump.common.defs.PumpType;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.RileyLinkUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(PowerMockRunner.class)
public class OmnipodPumpPluginTest {
@Mock HasAndroidInjector injector;
AAPSLogger aapsLogger = new AAPSLoggerTest();
RxBusWrapper rxBusWrapper = new RxBusWrapper();
@Mock ResourceHelper resourceHelper;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) ActivePluginProvider activePluginProvider;
@Mock AapsOmnipodManager aapsOmnipodManager;
@Mock CommandQueueProvider commandQueueProvider;
@Mock RileyLinkUtil rileyLinkUtil;
@Test
@PrepareForTest(Looper.class)
public <T> void testSetTempBasalPercent() {
DateTimeZone.setProvider(new UTCProvider());
// mock all the things
PowerMockito.mockStatic(Looper.class);
OmnipodPumpPlugin plugin = new OmnipodPumpPlugin(injector, aapsLogger, rxBusWrapper, null,
resourceHelper, activePluginProvider, null, null, aapsOmnipodManager, commandQueueProvider,
null, null, null, null, null,
rileyLinkUtil, null, null
);
when(activePluginProvider.getActiveTreatments().getTempBasalFromHistory(anyLong())).thenReturn(null);
when(rileyLinkUtil.getRileyLinkHistory()).thenReturn(new ArrayList<>());
when(injector.androidInjector()).thenReturn(new AndroidInjector<Object>() {
@Override public void inject(Object instance) {
}
});
Profile profile = mock(Profile.class);
// always return a PumpEnactResult containing same rate and duration as input
when(aapsOmnipodManager.setTemporaryBasal(any(TempBasalPair.class))).thenAnswer(
invocation -> {
TempBasalPair pair = invocation.getArgument(0);
PumpEnactResult result = new PumpEnactResult(injector);
result.absolute(pair.getInsulinRate());
result.duration(pair.getDurationMinutes());
return result;
});
// Given standard basal
when(profile.getBasal()).thenReturn(0.5d);
// When
PumpEnactResult result1 = plugin.setTempBasalPercent(80, 30, profile, false);
PumpEnactResult result2 = plugin.setTempBasalPercent(5000, 30000, profile, false);
PumpEnactResult result3 = plugin.setTempBasalPercent(0, 30, profile, false);
PumpEnactResult result4 = plugin.setTempBasalPercent(0, 0, profile, false);
PumpEnactResult result5 = plugin.setTempBasalPercent(-50, -1, profile, false);
// Then return correct values
assertEquals(result1.absolute, 0.4d, 0.01d);
assertEquals(result1.duration, 30);
assertEquals(result2.absolute, 25d, 0.01d);
assertEquals(result2.duration, 30000);
assertEquals(result3.absolute, 0d, 0.01d);
assertEquals(result3.duration, 30);
assertEquals(result4.absolute, 0d, 0.01d);
assertEquals(result4.duration, 0);
// this is validated downstream, see TempBasalExtraCommand
assertEquals(result5.absolute, -0.25d, 0.01d);
assertEquals(result5.duration, -1);
// Given zero basal
when(profile.getBasal()).thenReturn(0d);
// When
result1 = plugin.setTempBasalPercent(8000, 10, profile, false);
result2 = plugin.setTempBasalPercent(0, 00, profile, false);
// Then return zero values
assertEquals(result1.absolute, 0d, 0.01d);
assertEquals(result1.duration, 10);
assertEquals(result2.absolute, 0d, 0.01d);
assertEquals(result2.duration, 0);
// Given unhealthy basal
when(profile.getBasal()).thenReturn(500d);
// When treatment
result1 = plugin.setTempBasalPercent(80, 30, profile, false);
// Then return sane values
assertEquals(result1.absolute, PumpType.Insulet_Omnipod.determineCorrectBasalSize(500d * 0.8), 0.01d);
assertEquals(result1.duration, 30);
// Given weird basal
when(profile.getBasal()).thenReturn(1.234567d);
// When treatment
result1 = plugin.setTempBasalPercent(280, 500, profile, false);
// Then return sane values
assertEquals(result1.absolute, 3.4567876, 0.01d);
assertEquals(result1.duration, 500);
// Given negative basal
when(profile.getBasal()).thenReturn(-1.234567d);
// When treatment
result1 = plugin.setTempBasalPercent(280, 500, profile, false);
// Then return negative value (this is validated further downstream, see TempBasalExtraCommand)
assertEquals(result1.absolute, -3.4567876, 0.01d);
assertEquals(result1.duration, 500);
}
}

View file

@ -24,7 +24,7 @@ public class GetStatusCommandTest {
@Test
public void testPodInfoTypeFaultEvents() {
GetStatusCommand getStatusCommand = new GetStatusCommand(PodInfoType.FAULT_EVENT);
GetStatusCommand getStatusCommand = new GetStatusCommand(PodInfoType.DETAILED_STATUS);
assertArrayEquals(ByteUtil.fromHexString("0e0102"), getStatusCommand.getRawData());
}

View file

@ -0,0 +1,162 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo;
import org.joda.time.Duration;
import org.junit.Test;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.ErrorEventInfo;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
// From https://github.com/ps2/rileylink_ios/blob/omnipod-testing/OmniKitTests/PodInfoTests.swift
public class PodInfoDetailedStatusTest {
@Test
public void testPodInfoFaultEventNoFaultAlerts() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("02080100000a003800000003ff008700000095ff0000"));
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.NORMAL, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(0, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001);
assertEquals(0x0a, podInfoDetailedStatus.getPodMessageCounter());
assertNull(podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.ZERO.isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertTrue(Duration.standardSeconds(8100).isEqual(podInfoDetailedStatus.getTimeActive()));
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertNull(errorEventInfo);
assertNull(podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(2, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(21, podInfoDetailedStatus.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventDeliveryErrorDuringPriming() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("020f0000000900345c000103ff0001000005ae056029"));
assertEquals(PodProgressStatus.INACTIVE, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(0, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001);
assertEquals(0x09, podInfoDetailedStatus.getPodMessageCounter());
assertEquals(FaultEventCode.PRIME_OPEN_COUNT_TOO_LOW, podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.standardSeconds(60).isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertTrue(Duration.standardSeconds(60).isEqual(podInfoDetailedStatus.getTimeActive()));
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertFalse(errorEventInfo.isInsulinStateTableCorruption());
assertEquals(0x00, errorEventInfo.getInternalVariable());
assertFalse(errorEventInfo.isImmediateBolusInProgress());
assertEquals(PodProgressStatus.PRIMING_COMPLETED, errorEventInfo.getPodProgressStatus());
assertEquals(PodProgressStatus.PRIMING_COMPLETED, podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(2, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(46, podInfoDetailedStatus.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventErrorShuttingDown() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("020d0000000407f28609ff03ff0a0200000823080000"));
assertEquals(PodProgressStatus.FAULT_EVENT_OCCURRED, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(2034, podInfoDetailedStatus.getTicksDelivered());
assertEquals(101.7, podInfoDetailedStatus.getInsulinDelivered(), 0.000001);
assertEquals(0, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001);
assertEquals(0x04, podInfoDetailedStatus.getPodMessageCounter());
assertEquals(FaultEventCode.BASAL_OVER_INFUSION_PULSE, podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.standardMinutes(2559).isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertFalse(errorEventInfo.isInsulinStateTableCorruption());
assertEquals(0x00, errorEventInfo.getInternalVariable());
assertFalse(errorEventInfo.isImmediateBolusInProgress());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, errorEventInfo.getPodProgressStatus());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(0, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(35, podInfoDetailedStatus.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventInsulinNotDelivered() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("020f0000010200ec6a026803ff026b000028a7082023"));
assertEquals(PodProgressStatus.INACTIVE, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(236, podInfoDetailedStatus.getTicksDelivered());
assertEquals(11.8, podInfoDetailedStatus.getInsulinDelivered(), 0.000001);
assertEquals(0.05, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001);
assertEquals(0x02, podInfoDetailedStatus.getPodMessageCounter());
assertEquals(FaultEventCode.OCCLUSION_CHECK_ABOVE_THRESHOLD, podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.standardMinutes(616).isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertFalse(errorEventInfo.isInsulinStateTableCorruption());
assertEquals(0x01, errorEventInfo.getInternalVariable());
assertFalse(errorEventInfo.isImmediateBolusInProgress());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, errorEventInfo.getPodProgressStatus());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(2, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(39, podInfoDetailedStatus.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventMaxBolusNotDelivered() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("020f00ffff0200ec6a026803ff026b000028a7082023"));
assertEquals(PodProgressStatus.INACTIVE, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(236, podInfoDetailedStatus.getTicksDelivered());
assertEquals(11.8, podInfoDetailedStatus.getInsulinDelivered(), 0.000001);
assertEquals(3276.75, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001); // Insane and will not happen, but this verifies that we convert it to an unsigned int
assertEquals(0x02, podInfoDetailedStatus.getPodMessageCounter());
assertEquals(FaultEventCode.OCCLUSION_CHECK_ABOVE_THRESHOLD, podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.standardMinutes(616).isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertFalse(errorEventInfo.isInsulinStateTableCorruption());
assertEquals(0x01, errorEventInfo.getInternalVariable());
assertFalse(errorEventInfo.isImmediateBolusInProgress());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, errorEventInfo.getPodProgressStatus());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(2, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(39, podInfoDetailedStatus.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventInsulinStateTableCorruptionFoundDuringErrorLogging() {
PodInfoDetailedStatus podInfoDetailedStatus = new PodInfoDetailedStatus(ByteUtil.fromHexString("020D00000000000012FFFF03FF00160000879A070000"));
assertEquals(PodProgressStatus.FAULT_EVENT_OCCURRED, podInfoDetailedStatus.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoDetailedStatus.getDeliveryStatus());
assertEquals(0, podInfoDetailedStatus.getBolusNotDelivered(), 0.000001);
assertEquals(0x00, podInfoDetailedStatus.getPodMessageCounter());
assertEquals(FaultEventCode.RESET_DUE_TO_LVD, podInfoDetailedStatus.getFaultEventCode());
assertTrue(Duration.ZERO.isEqual(podInfoDetailedStatus.getFaultEventTime()));
assertNull(podInfoDetailedStatus.getReservoirLevel());
assertTrue(Duration.standardSeconds(1320).isEqual(podInfoDetailedStatus.getTimeActive()));
assertEquals(0, podInfoDetailedStatus.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoDetailedStatus.isFaultAccessingTables());
ErrorEventInfo errorEventInfo = podInfoDetailedStatus.getErrorEventInfo();
assertTrue(errorEventInfo.isInsulinStateTableCorruption());
assertEquals(0x00, errorEventInfo.getInternalVariable());
assertFalse(errorEventInfo.isImmediateBolusInProgress());
assertEquals(PodProgressStatus.INSERTING_CANNULA, errorEventInfo.getPodProgressStatus());
assertEquals(PodProgressStatus.INSERTING_CANNULA, podInfoDetailedStatus.getPreviousPodProgressStatus());
assertEquals(2, podInfoDetailedStatus.getReceiverLowGain());
assertEquals(26, podInfoDetailedStatus.getRadioRSSI());
}
}

View file

@ -1,121 +0,0 @@
package info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.response.podinfo;
import org.joda.time.Duration;
import org.junit.Test;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.DeliveryStatus;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FaultEventCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.LogEventErrorCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
// From https://github.com/ps2/rileylink_ios/blob/omnipod-testing/OmniKitTests/PodInfoTests.swift
public class PodInfoFaultEventTest {
@Test
public void testPodInfoFaultEventNoFaultAlerts() {
PodInfoFaultEvent podInfoFaultEvent = new PodInfoFaultEvent(ByteUtil.fromHexString("02080100000a003800000003ff008700000095ff0000"));
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoFaultEvent.getPodProgressStatus());
assertEquals(DeliveryStatus.NORMAL, podInfoFaultEvent.getDeliveryStatus());
assertEquals(0, podInfoFaultEvent.getBolusNotDelivered(), 0.000001);
assertEquals(0x0a, podInfoFaultEvent.getPodMessageCounter());
assertEquals(FaultEventCode.NO_FAULTS, podInfoFaultEvent.getFaultEventCode());
assertTrue(Duration.ZERO.isEqual(podInfoFaultEvent.getFaultEventTime()));
assertNull(podInfoFaultEvent.getReservoirLevel());
assertTrue(Duration.standardSeconds(8100).isEqual(podInfoFaultEvent.getTimeActive()));
assertEquals(0, podInfoFaultEvent.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoFaultEvent.isFaultAccessingTables());
assertEquals(LogEventErrorCode.NONE, podInfoFaultEvent.getLogEventErrorType());
assertEquals(PodProgressStatus.INACTIVE, podInfoFaultEvent.getPodProgressStatusAtTimeOfFirstLoggedFaultEvent());
assertEquals(2, podInfoFaultEvent.getReceiverLowGain());
assertEquals(21, podInfoFaultEvent.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventDeliveryErrorDuringPriming() {
PodInfoFaultEvent podInfoFaultEvent = new PodInfoFaultEvent(ByteUtil.fromHexString("020f0000000900345c000103ff0001000005ae056029"));
assertEquals(PodProgressStatus.INACTIVE, podInfoFaultEvent.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoFaultEvent.getDeliveryStatus());
assertEquals(0, podInfoFaultEvent.getBolusNotDelivered(), 0.000001);
assertEquals(0x09, podInfoFaultEvent.getPodMessageCounter());
assertEquals(FaultEventCode.PRIME_OPEN_COUNT_TOO_LOW, podInfoFaultEvent.getFaultEventCode());
assertTrue(Duration.standardSeconds(60).isEqual(podInfoFaultEvent.getFaultEventTime()));
assertNull(podInfoFaultEvent.getReservoirLevel());
assertTrue(Duration.standardSeconds(60).isEqual(podInfoFaultEvent.getTimeActive()));
assertEquals(0, podInfoFaultEvent.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoFaultEvent.isFaultAccessingTables());
assertEquals(LogEventErrorCode.NONE, podInfoFaultEvent.getLogEventErrorType());
assertEquals(PodProgressStatus.PRIMING_COMPLETED, podInfoFaultEvent.getPodProgressStatusAtTimeOfFirstLoggedFaultEvent());
assertEquals(2, podInfoFaultEvent.getReceiverLowGain());
assertEquals(46, podInfoFaultEvent.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventErrorShuttingDown() {
PodInfoFaultEvent podInfoFaultEvent = new PodInfoFaultEvent(ByteUtil.fromHexString("020d0000000407f28609ff03ff0a0200000823080000"));
assertEquals(PodProgressStatus.FAULT_EVENT_OCCURRED, podInfoFaultEvent.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoFaultEvent.getDeliveryStatus());
assertEquals(2034, podInfoFaultEvent.getTicksDelivered());
assertEquals(101.7, podInfoFaultEvent.getInsulinDelivered(), 0.000001);
assertEquals(0, podInfoFaultEvent.getBolusNotDelivered(), 0.000001);
assertEquals(0x04, podInfoFaultEvent.getPodMessageCounter());
assertEquals(FaultEventCode.BASAL_OVER_INFUSION_PULSE, podInfoFaultEvent.getFaultEventCode());
assertTrue(Duration.standardMinutes(2559).isEqual(podInfoFaultEvent.getFaultEventTime()));
assertNull(podInfoFaultEvent.getReservoirLevel());
assertEquals(0, podInfoFaultEvent.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoFaultEvent.isFaultAccessingTables());
assertEquals(LogEventErrorCode.NONE, podInfoFaultEvent.getLogEventErrorType());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoFaultEvent.getPodProgressStatusAtTimeOfFirstLoggedFaultEvent());
assertEquals(0, podInfoFaultEvent.getReceiverLowGain());
assertEquals(35, podInfoFaultEvent.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventInsulinNotDelivered() {
PodInfoFaultEvent podInfoFaultEvent = new PodInfoFaultEvent(ByteUtil.fromHexString("020f0000010200ec6a026803ff026b000028a7082023"));
assertEquals(PodProgressStatus.INACTIVE, podInfoFaultEvent.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoFaultEvent.getDeliveryStatus());
assertEquals(236, podInfoFaultEvent.getTicksDelivered());
assertEquals(11.8, podInfoFaultEvent.getInsulinDelivered(), 0.000001);
assertEquals(0.05, podInfoFaultEvent.getBolusNotDelivered(), 0.000001);
assertEquals(0x02, podInfoFaultEvent.getPodMessageCounter());
assertEquals(FaultEventCode.OCCLUSION_CHECK_ABOVE_THRESHOLD, podInfoFaultEvent.getFaultEventCode());
assertTrue(Duration.standardMinutes(616).isEqual(podInfoFaultEvent.getFaultEventTime()));
assertNull(podInfoFaultEvent.getReservoirLevel());
assertEquals(0, podInfoFaultEvent.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoFaultEvent.isFaultAccessingTables());
assertEquals(LogEventErrorCode.INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2, podInfoFaultEvent.getLogEventErrorType());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoFaultEvent.getPodProgressStatusAtTimeOfFirstLoggedFaultEvent());
assertEquals(2, podInfoFaultEvent.getReceiverLowGain());
assertEquals(39, podInfoFaultEvent.getRadioRSSI());
}
@Test
public void testPodInfoFaultEventMaxBolusNotDelivered() {
PodInfoFaultEvent podInfoFaultEvent = new PodInfoFaultEvent(ByteUtil.fromHexString("020f00ffff0200ec6a026803ff026b000028a7082023"));
assertEquals(PodProgressStatus.INACTIVE, podInfoFaultEvent.getPodProgressStatus());
assertEquals(DeliveryStatus.SUSPENDED, podInfoFaultEvent.getDeliveryStatus());
assertEquals(236, podInfoFaultEvent.getTicksDelivered());
assertEquals(11.8, podInfoFaultEvent.getInsulinDelivered(), 0.000001);
assertEquals(3276.75, podInfoFaultEvent.getBolusNotDelivered(), 0.000001); // Insane and will not happen, but this verifies that we convert it to an unsigned int
assertEquals(0x02, podInfoFaultEvent.getPodMessageCounter());
assertEquals(FaultEventCode.OCCLUSION_CHECK_ABOVE_THRESHOLD, podInfoFaultEvent.getFaultEventCode());
assertTrue(Duration.standardMinutes(616).isEqual(podInfoFaultEvent.getFaultEventTime()));
assertNull(podInfoFaultEvent.getReservoirLevel());
assertEquals(0, podInfoFaultEvent.getUnacknowledgedAlerts().getRawValue());
assertFalse(podInfoFaultEvent.isFaultAccessingTables());
assertEquals(LogEventErrorCode.INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2, podInfoFaultEvent.getLogEventErrorType());
assertEquals(PodProgressStatus.ABOVE_FIFTY_UNITS, podInfoFaultEvent.getPodProgressStatusAtTimeOfFirstLoggedFaultEvent());
assertEquals(2, podInfoFaultEvent.getReceiverLowGain());
assertEquals(39, podInfoFaultEvent.getRadioRSSI());
}
}

View file

@ -5,7 +5,6 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import info.nightscout.androidaps.plugins.pump.common.utils.ByteUtil;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.LogEventErrorCode;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodInfoType;
import static org.junit.Assert.assertArrayEquals;
@ -39,18 +38,18 @@ public class PodInfoResponseTest {
public void testMessageDecoding() {
PodInfoResponse podInfoResponse = new PodInfoResponse(ByteUtil.fromHexString("0216020d0000000000ab6a038403ff03860000285708030d"));
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
assertEquals(PodInfoType.DETAILED_STATUS, podInfoResponse.getSubType());
PodInfoFaultEvent podInfo = (PodInfoFaultEvent) podInfoResponse.getPodInfo();
PodInfoDetailedStatus podInfo = (PodInfoDetailedStatus) podInfoResponse.getPodInfo();
assertFalse(podInfo.isFaultAccessingTables());
assertEquals(LogEventErrorCode.INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2, podInfo.getLogEventErrorType());
assertEquals(0x01, podInfo.getErrorEventInfo().getInternalVariable());
}
@Test
public void testInvalidPodInfoTypeMessageDecoding() {
PodInfoResponse podInfoResponse = new PodInfoResponse(ByteUtil.fromHexString("0216020d0000000000ab6a038403ff03860000285708030d"));
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
assertEquals(PodInfoType.DETAILED_STATUS, podInfoResponse.getSubType());
thrown.expect(ClassCastException.class);
PodInfoActiveAlerts podInfo = (PodInfoActiveAlerts) podInfoResponse.getPodInfo();

View file

@ -1,12 +1,15 @@
package info.nightscout.androidaps.plugins.pump.common.events
import info.nightscout.androidaps.events.Event
import info.nightscout.androidaps.events.EventStatus
import info.nightscout.androidaps.plugins.pump.common.defs.PumpDeviceState
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice
import info.nightscout.androidaps.utils.resources.ResourceHelper
open class EventRileyLinkDeviceStatusChange : Event {
open class EventRileyLinkDeviceStatusChange : EventStatus {
var rileyLinkTargetDevice: RileyLinkTargetDevice? = null
var rileyLinkServiceState: RileyLinkServiceState? = null
var rileyLinkError: RileyLinkError? = null
@ -14,11 +17,10 @@ open class EventRileyLinkDeviceStatusChange : Event {
var errorDescription: String? = null
constructor() {
}
constructor(rileyLinkServiceState: RileyLinkServiceState?, rileyLinkError: RileyLinkError?) {
constructor(rileyLinkTargetDevice: RileyLinkTargetDevice, rileyLinkServiceState: RileyLinkServiceState?, rileyLinkError: RileyLinkError?) {
this.rileyLinkTargetDevice = rileyLinkTargetDevice
this.rileyLinkServiceState = rileyLinkServiceState
this.rileyLinkError = rileyLinkError
}
@ -31,4 +33,17 @@ open class EventRileyLinkDeviceStatusChange : Event {
this.pumpDeviceState = pumpDeviceState
this.errorDescription = errorDescription
}
override fun getStatus(resourceHelper: ResourceHelper): String {
val rileyLinkServiceState = this.rileyLinkServiceState ?: return ""
val resourceId = rileyLinkServiceState.resourceId
val rileyLinkError = this.rileyLinkError
if (rileyLinkServiceState.isError && rileyLinkError != null) {
val rileyLinkTargetDevice = this.rileyLinkTargetDevice ?: return ""
return resourceHelper.gs(rileyLinkError.getResourceId(rileyLinkTargetDevice))
}
return resourceHelper.gs(resourceId)
}
}

View file

@ -24,8 +24,6 @@ import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.ble.defs.Rile
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkError;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkServiceState;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.defs.RileyLinkTargetDevice;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceResult;
import info.nightscout.androidaps.plugins.pump.common.hw.rileylink.service.data.ServiceTransport;
import info.nightscout.androidaps.utils.resources.ResourceHelper;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -114,6 +112,10 @@ public abstract class RileyLinkService extends DaggerService {
public abstract RileyLinkCommunicationManager getDeviceCommunicationManager();
public RileyLinkServiceState getRileyLinkServiceState() {
return rileyLinkServiceData == null ? null : rileyLinkServiceData.rileyLinkServiceState;
}
// Here is where the wake-lock begins:
// We've received a service startCommand, we grab the lock.
@Override

View file

@ -33,8 +33,7 @@ public class RileyLinkServiceData {
public RileyLinkServiceState rileyLinkServiceState = RileyLinkServiceState.NotStarted;
private long lastServiceStateChange = 0L;
public RileyLinkFirmwareVersion firmwareVersion;
public RileyLinkTargetFrequency rileyLinkTargetFrequency; // TODO this might not be correct place
public RileyLinkTargetFrequency rileyLinkTargetFrequency;
public String rileylinkAddress;
long lastTuneUpTime = 0L;
public Double lastGoodFrequency;
@ -44,7 +43,7 @@ public class RileyLinkServiceData {
// radio version
public RileyLinkFirmwareVersion versionCC110;
public RileyLinkTargetDevice targetDevice; // TODO this might not be correct place
public RileyLinkTargetDevice targetDevice;
// Medtronic Pump
public String pumpID;
@ -86,7 +85,7 @@ public class RileyLinkServiceData {
aapsLogger.info(LTag.PUMP, "RileyLink State Changed: {} {}", newState, errorCode == null ? "" : " - Error State: " + errorCode.name());
rileyLinkUtil.getRileyLinkHistory().add(new RLHistoryItem(rileyLinkServiceState, errorCode, targetDevice));
rxBus.send(new EventRileyLinkDeviceStatusChange(newState, errorCode));
rxBus.send(new EventRileyLinkDeviceStatusChange(targetDevice, newState, errorCode));
return null;
} else {