Fix automatically updating from PodInfoFaultEvent and improve detecting cancelled TBRs

This commit is contained in:
Bart Sopers 2020-09-02 01:26:36 +02:00
parent fead64aa16
commit 68ff228426
12 changed files with 146 additions and 73 deletions

View file

@ -77,6 +77,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
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.event.EventOmnipodTbrChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.rileylink.service.RileyLinkOmnipodService;
import info.nightscout.androidaps.plugins.pump.omnipod.ui.OmnipodFragment;
@ -254,6 +255,11 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
.observeOn(Schedulers.io())
.subscribe(event -> context.unbindService(serviceConnection), fabricPrivacy::logException)
);
disposables.add(rxBus
.toObservable(EventOmnipodTbrChanged.class)
.observeOn(Schedulers.io())
.subscribe(event -> updateAapsTbr(), fabricPrivacy::logException)
);
disposables.add(rxBus
.toObservable(EventPreferenceChange.class)
.observeOn(Schedulers.io())
@ -291,6 +297,17 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
);
}
private void updateAapsTbr() {
// As per the characteristics of the Omnipod, we only know whether or not a TBR is currently active
// But it doesn't tell us the duration or amount, so we can only update TBR status in AAPS if
// The pod is not running a TBR, while AAPS thinks it is
if (!podStateManager.hasTempBasal()) {
if (activePlugin.getActiveTreatments().isTempBasalInProgress()) {
aapsOmnipodManager.reportCancelledTbr();
}
}
}
@Override
protected void onStop() {
super.onStop();

View file

@ -46,7 +46,7 @@ public class SetTempBasalAction implements OmnipodAction<StatusResponse> {
OmnipodMessage message = new OmnipodMessage(podStateManager.getAddress(), messageBlocks, podStateManager.getMessageNumber());
StatusResponse statusResponse = communicationService.exchangeMessages(StatusResponse.class, podStateManager, message);
podStateManager.setLastTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration);
podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration);
return statusResponse;
}
}

View file

@ -21,8 +21,8 @@ public class PodInfoResponse extends MessageBlock {
return subType;
}
public <T extends PodInfo> T getPodInfo() {
return (T) podInfo;
public PodInfo getPodInfo() {
return podInfo;
}
@Override

View file

@ -331,13 +331,14 @@ public class OmnipodManager {
commandDeliveryStatus = CommandDeliveryStatus.UNCERTAIN_FAILURE;
}
DateTime startDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
podStateManager.setLastBolus(startDate, units);
CompositeDisposable disposables = new CompositeDisposable();
Duration bolusDuration = calculateBolusDuration(units, OmnipodConstants.POD_BOLUS_DELIVERY_RATE);
Duration estimatedRemainingBolusDuration = bolusDuration.minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
DateTime startDate = DateTime.now().minus(OmnipodConstants.AVERAGE_BOLUS_COMMAND_COMMUNICATION_DURATION);
podStateManager.setLastBolus(startDate, units, estimatedRemainingBolusDuration);
CompositeDisposable disposables = new CompositeDisposable();
if (progressIndicationConsumer != null) {
int numberOfProgressReports = Math.max(20, Math.min(100, (int) Math.ceil(units) * 10));
long progressReportInterval = estimatedRemainingBolusDuration.getMillis() / numberOfProgressReports;
@ -427,7 +428,7 @@ public class OmnipodManager {
private void discardActiveBolusData(double bolusNotDelivered) {
synchronized (bolusDataMutex) {
double unitsDelivered = activeBolusData.getUnits() - bolusNotDelivered;
podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered);
podStateManager.setLastBolus(activeBolusData.getStartDate(), unitsDelivered, new Duration(activeBolusData.getStartDate(), DateTime.now()));
activeBolusData.getDisposables().dispose();
activeBolusData.getBolusCompletionSubject().onSuccess(new BolusDeliveryResult(unitsDelivered));
activeBolusData = null;
@ -505,7 +506,7 @@ public class OmnipodManager {
// Try to get pulse log for diagnostics
try {
PodInfoResponse podInfoResponse = communicationService.executeAction(new GetPodInfoAction(podStateManager, PodInfoType.RECENT_PULSE_LOG));
PodInfoRecentPulseLog pulseLogInfo = podInfoResponse.getPodInfo();
PodInfoRecentPulseLog pulseLogInfo = (PodInfoRecentPulseLog) podInfoResponse.getPodInfo();
aapsLogger.info(LTag.PUMPCOMM, "Retrieved pulse log from the pod: {}", pulseLogInfo.toString());
} catch (Exception ex) {
aapsLogger.warn(LTag.PUMPCOMM, "Failed to retrieve pulse log from the pod", ex);

View file

@ -15,6 +15,7 @@ import org.joda.time.format.ISODateTimeFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;
import info.nightscout.androidaps.logging.AAPSLogger;
@ -352,31 +353,56 @@ public abstract class PodStateManager {
return getSafe(() -> podState.getLastBolusAmount());
}
public final void setLastBolus(DateTime startTime, double amount) {
public final Duration getLastBolusDuration() {
return getSafe(() -> podState.getLastBolusDuration());
}
public final void setLastBolus(DateTime startTime, double amount, Duration duration) {
setAndStore(() -> {
podState.setLastBolusStartTime(startTime);
podState.setLastBolusAmount(amount);
podState.setLastBolusDuration(duration);
});
}
public final DateTime getLastTempBasalStartTime() {
return getSafe(() -> podState.getLastTempBasalStartTime());
public final DateTime getTempBasalStartTime() {
return getSafe(() -> podState.getTempBasalStartTime());
}
public final Double getLastTempBasalAmount() {
return getSafe(() -> podState.getLastTempBasalAmount());
public final Double getTempBasalAmount() {
return getSafe(() -> podState.getTempBasalAmount());
}
public final Duration getLastTempBasalDuration() {
return getSafe(() -> podState.getLastTempBasalDuration());
public final Duration getTempBasalDuration() {
return getSafe(() -> podState.getTempBasalDuration());
}
public final void setLastTempBasal(DateTime startTime, Double amount, Duration duration) {
setAndStore(() -> {
podState.setLastTempBasalStartTime(startTime);
podState.setLastTempBasalAmount(amount);
podState.setLastTempBasalDuration(duration);
});
public final void setTempBasal(DateTime startTime, Double amount, Duration duration) {
setTempBasal(startTime, amount, duration, true);
}
public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean store) {
DateTime currentStartTime = getTempBasalStartTime();
Double currentAmount = getTempBasalAmount();
Duration currentDuration = getTempBasalDuration();
if (!Objects.equals(currentStartTime, startTime) || !Objects.equals(currentAmount, amount) || !Objects.equals(currentDuration, duration)) {
Runnable runnable = () -> {
podState.setTempBasalStartTime(startTime);
podState.setTempBasalAmount(amount);
podState.setTempBasalDuration(duration);
};
if (store) {
setAndStore(runnable);
} else {
setSafe(runnable);
}
onTbrChanged();
}
}
public final boolean hasTempBasal() {
return getTempBasalAmount() != null && getTempBasalDuration() != null && getTempBasalStartTime() != null;
}
public final DeliveryStatus getLastDeliveryStatus() {
@ -405,14 +431,18 @@ public abstract class PodStateManager {
podState.setTotalTicksDelivered(statusResponse.getTicksDelivered());
podState.setPodProgressStatus(statusResponse.getPodProgressStatus());
if (!statusResponse.getDeliveryStatus().isTbrRunning()) {
podState.setLastTempBasalStartTime(null);
podState.setLastTempBasalAmount(null);
podState.setLastTempBasalDuration(null);
// Triggers {@link #onTbrChanged() onTbrChanged()} when appropriate
setTempBasal(null, null, null, false);
}
podState.setLastUpdatedFromResponse(DateTime.now());
});
}
protected void onTbrChanged() {
// Deliberately left empty
// Can be overridden in subclasses
}
private void setAndStore(Runnable runnable) {
setSafe(runnable);
storePodState();
@ -507,9 +537,10 @@ public abstract class PodStateManager {
private BasalSchedule basalSchedule;
private DateTime lastBolusStartTime;
private Double lastBolusAmount;
private Double lastTempBasalAmount;
private DateTime lastTempBasalStartTime;
private Duration lastTempBasalDuration;
private Duration lastBolusDuration;
private Double tempBasalAmount;
private DateTime tempBasalStartTime;
private Duration tempBasalDuration;
private final Map<AlertSlot, AlertType> configuredAlerts = new HashMap<>();
private PodState(int address) {
@ -712,28 +743,36 @@ public abstract class PodStateManager {
this.lastBolusAmount = lastBolusAmount;
}
Double getLastTempBasalAmount() {
return lastTempBasalAmount;
Duration getLastBolusDuration() {
return lastBolusDuration;
}
void setLastTempBasalAmount(Double lastTempBasalAmount) {
this.lastTempBasalAmount = lastTempBasalAmount;
void setLastBolusDuration(Duration lastBolusDuration) {
this.lastBolusDuration = lastBolusDuration;
}
DateTime getLastTempBasalStartTime() {
return lastTempBasalStartTime;
Double getTempBasalAmount() {
return tempBasalAmount;
}
void setLastTempBasalStartTime(DateTime lastTempBasalStartTime) {
this.lastTempBasalStartTime = lastTempBasalStartTime;
void setTempBasalAmount(Double tempBasalAmount) {
this.tempBasalAmount = tempBasalAmount;
}
Duration getLastTempBasalDuration() {
return lastTempBasalDuration;
DateTime getTempBasalStartTime() {
return tempBasalStartTime;
}
void setLastTempBasalDuration(Duration lastTempBasalDuration) {
this.lastTempBasalDuration = lastTempBasalDuration;
void setTempBasalStartTime(DateTime tempBasalStartTime) {
this.tempBasalStartTime = tempBasalStartTime;
}
Duration getTempBasalDuration() {
return tempBasalDuration;
}
void setTempBasalDuration(Duration tempBasalDuration) {
this.tempBasalDuration = tempBasalDuration;
}
Map<AlertSlot, AlertType> getConfiguredAlerts() {
@ -766,9 +805,10 @@ public abstract class PodStateManager {
", basalSchedule=" + basalSchedule +
", lastBolusStartTime=" + lastBolusStartTime +
", lastBolusAmount=" + lastBolusAmount +
", lastTempBasalAmount=" + lastTempBasalAmount +
", lastTempBasalStartTime=" + lastTempBasalStartTime +
", lastTempBasalDuration=" + lastTempBasalDuration +
", lastBolusDuration=" + lastBolusDuration +
", tempBasalAmount=" + tempBasalAmount +
", tempBasalStartTime=" + tempBasalStartTime +
", tempBasalDuration=" + tempBasalDuration +
", configuredAlerts=" + configuredAlerts +
'}';
}

View file

@ -0,0 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.event
import info.nightscout.androidaps.events.Event
/**
* Created by andy on 04.06.2018.
*/
class EventOmnipodTbrChanged : Event()

View file

@ -24,7 +24,6 @@ import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.Event;
import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.DatabaseHelperInterface;
import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
@ -245,11 +244,8 @@ public class AapsOmnipodManager {
delegate.setBasalSchedule(basalSchedule, isBasalBeepsEnabled());
time = System.currentTimeMillis();
// Because setting a basal profile actually suspends and then resumes delivery, TBR is implicitly cancelled
if (historyEntryType == PodHistoryEntryType.RESUME_DELIVERY) {
cancelSuspendedFakeTbrIfExists();
} else {
reportImplicitlyCancelledTbr(time - 1000);
}
addSuccessToHistory(time, historyEntryType, profile.getBasalValues());
} catch (CommandFailedAfterChangingDeliveryStatusException ex) {
@ -402,7 +398,6 @@ public class AapsOmnipodManager {
delegate.setTemporaryBasal(PumpType.Insulet_Omnipod.determineCorrectBasalSize(tempBasalPair.getInsulinRate()), Duration.standardMinutes(tempBasalPair.getDurationMinutes()), beepsEnabled, beepsEnabled);
time = System.currentTimeMillis();
} catch (CommandFailedAfterChangingDeliveryStatusException ex) {
reportImplicitlyCancelledTbr(time);
String comment = getStringResource(R.string.omnipod_cancelled_old_tbr_failed_to_set_new);
addFailureToHistory(time, PodHistoryEntryType.SET_TEMPORARY_BASAL, comment);
return new PumpEnactResult(injector).success(false).enacted(false).comment(comment);
@ -482,8 +477,6 @@ public class AapsOmnipodManager {
try {
delegate.setTime(isBasalBeepsEnabled());
time = System.currentTimeMillis();
// Because set time actually suspends and then resumes delivery, TBR is implicitly cancelled
reportImplicitlyCancelledTbr(time - 1000);
addSuccessToHistory(time, PodHistoryEntryType.SET_TIME, null);
} catch (CommandFailedAfterChangingDeliveryStatusException ex) {
createSuspendedFakeTbrIfNotExists();
@ -507,7 +500,7 @@ public class AapsOmnipodManager {
public PodInfoRecentPulseLog readPulseLog() {
PodInfoResponse response = delegate.getPodInfo(PodInfoType.RECENT_PULSE_LOG);
return response.getPodInfo();
return (PodInfoRecentPulseLog) response.getPodInfo();
}
public OmnipodRileyLinkCommunicationManager getCommunicationService() {
@ -593,21 +586,19 @@ public class AapsOmnipodManager {
return false;
}
private void reportImplicitlyCancelledTbr(long time) {
TreatmentsInterface plugin = activePlugin.getActiveTreatments();
if (plugin.isTempBasalInProgress()) {
aapsLogger.debug(LTag.PUMP, "Reporting implicitly cancelled TBR to Treatments plugin");
public void reportCancelledTbr() {
long time = System.currentTimeMillis();
aapsLogger.debug(LTag.PUMP, "Reporting cancelled TBR to AAPS");
long pumpId = addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null);
long pumpId = addSuccessToHistory(time, PodHistoryEntryType.CANCEL_TEMPORARY_BASAL_BY_DRIVER, null);
TemporaryBasal temporaryBasal = new TemporaryBasal(injector) //
.date(time) //
.duration(0) //
.source(Source.PUMP) //
.pumpId(pumpId);
TemporaryBasal temporaryBasal = new TemporaryBasal(injector) //
.date(time) //
.duration(0) //
.source(Source.PUMP) //
.pumpId(pumpId);
plugin.addToHistoryTempBasal(temporaryBasal);
}
activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal);
}
private long addSuccessToHistory(long requestTime, PodHistoryEntryType entryType, Object data) {

View file

@ -4,18 +4,22 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.pump.omnipod.definition.OmnipodStorageKeys;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateManager;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton
public class AapsPodStateManager extends PodStateManager {
private final SP sp;
private final RxBusWrapper rxBus;
@Inject
public AapsPodStateManager(AAPSLogger aapsLogger, SP sp) {
public AapsPodStateManager(AAPSLogger aapsLogger, SP sp, RxBusWrapper rxBus) {
super(aapsLogger);
this.sp = sp;
this.rxBus = rxBus;
}
@Override
@ -27,4 +31,8 @@ public class AapsPodStateManager extends PodStateManager {
protected void storePodState(String podState) {
sp.putString(OmnipodStorageKeys.Preferences.POD_STATE, podState);
}
@Override protected void onTbrChanged() {
rxBus.send(new EventOmnipodTbrChanged());
}
}

View file

@ -23,6 +23,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.mess
import info.nightscout.androidaps.plugins.pump.omnipod.driver.communication.message.command.DeactivatePodCommand;
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.PodInfoResponse;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.MessageBlockType;
@ -127,6 +128,11 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
if (responseMessageBlock instanceof StatusUpdatableResponse) {
podStateManager.updateFromResponse((StatusUpdatableResponse) responseMessageBlock);
} else if (responseMessageBlock instanceof PodInfoResponse) {
PodInfo podInfo = ((PodInfoResponse) responseMessageBlock).getPodInfo();
if (podInfo instanceof StatusUpdatableResponse) {
podStateManager.updateFromResponse((StatusUpdatableResponse) podInfo);
}
}
if (responseClass.isInstance(responseMessageBlock)) {
@ -150,7 +156,7 @@ public class OmnipodRileyLinkCommunicationManager extends RileyLinkCommunication
throw new PodReturnedErrorResponseException(error);
}
} else if (responseMessageBlock.getType() == MessageBlockType.POD_INFO_RESPONSE && ((PodInfoResponse) responseMessageBlock).getSubType() == PodInfoType.FAULT_EVENT) {
PodInfoFaultEvent faultEvent = ((PodInfoResponse) responseMessageBlock).getPodInfo();
PodInfoFaultEvent faultEvent = (PodInfoFaultEvent) ((PodInfoResponse) responseMessageBlock).getPodInfo();
podStateManager.setFaultEvent(faultEvent);
podStateManager.setLastFailedCommunication(DateTime.now());
throw new PodFaultException(faultEvent);

View file

@ -265,9 +265,9 @@ class OmnipodFragment : DaggerFragment() {
}
// Temp basal
val lastTempBasalStartTime = podStateManager.lastTempBasalStartTime;
val lastTempBasalAmount = podStateManager.lastTempBasalAmount
val lastTempBasalDuration = podStateManager.lastTempBasalDuration;
val lastTempBasalStartTime = podStateManager.tempBasalStartTime;
val lastTempBasalAmount = podStateManager.tempBasalAmount
val lastTempBasalDuration = podStateManager.tempBasalDuration;
if (lastTempBasalStartTime != null && lastTempBasalAmount != null && lastTempBasalDuration != null) {
val endTime = lastTempBasalStartTime.plus(lastTempBasalDuration);
val minutesRunning = Duration(lastTempBasalStartTime, now).standardMinutes

View file

@ -41,7 +41,7 @@ public class PodInfoResponseTest {
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
PodInfoFaultEvent podInfo = podInfoResponse.getPodInfo();
PodInfoFaultEvent podInfo = (PodInfoFaultEvent) podInfoResponse.getPodInfo();
assertFalse(podInfo.isFaultAccessingTables());
assertEquals(LogEventErrorCode.INTERNAL_2_BIT_VARIABLE_SET_AND_MANIPULATED_IN_MAIN_LOOP_ROUTINES_2, podInfo.getLogEventErrorType());
}
@ -53,6 +53,6 @@ public class PodInfoResponseTest {
assertEquals(PodInfoType.FAULT_EVENT, podInfoResponse.getSubType());
thrown.expect(ClassCastException.class);
PodInfoActiveAlerts podInfo = podInfoResponse.getPodInfo();
PodInfoActiveAlerts podInfo = (PodInfoActiveAlerts) podInfoResponse.getPodInfo();
}
}

View file

@ -11,6 +11,7 @@ import org.mockito.Mock;
import org.powermock.modules.junit4.PowerMockRunner;
import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.FirmwareVersion;
import info.nightscout.androidaps.plugins.pump.omnipod.driver.definition.PodProgressStatus;
import info.nightscout.androidaps.utils.sharedPreferences.SP;
@ -21,6 +22,7 @@ import static org.junit.Assert.assertEquals;
public class AapsPodStateManagerTest {
@Mock AAPSLogger aapsLogger;
@Mock SP sp;
@Mock RxBusWrapper rxBus;
@Test
public void times() {
@ -31,7 +33,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);
@ -49,7 +51,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);
@ -72,7 +74,7 @@ public class AapsPodStateManagerTest {
DateTimeUtils.setCurrentMillisFixed(now.getMillis());
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp);
AapsPodStateManager podStateManager = new AapsPodStateManager(aapsLogger, sp, rxBus);
podStateManager.initState(0x0);
podStateManager.setInitializationParameters(0, 0, new FirmwareVersion(1, 1, 1),
new FirmwareVersion(2, 2, 2), timeZone, PodProgressStatus.ABOVE_FIFTY_UNITS);