Merge pull request #41 from AAPS-Omnipod/omnipod_eros_dev_prevent_0x31

Prevent 0x31 Pod faults (& improve recovery from uncertain delivery statuses)
This commit is contained in:
bartsopers 2020-11-22 21:41:25 +01:00 committed by GitHub
commit e24f8b3647
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 327 additions and 84 deletions

View file

@ -7,6 +7,7 @@ import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentService import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
@ -27,8 +28,10 @@ class TreatmentsPluginHistory @Inject constructor(
profileFunction: ProfileFunction, profileFunction: ProfileFunction,
activePlugin: ActivePluginProvider, activePlugin: ActivePluginProvider,
nsUpload: NSUpload, nsUpload: NSUpload,
fabricPrivacy: FabricPrivacy, dateUtil: DateUtil fabricPrivacy: FabricPrivacy,
) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil) { dateUtil: DateUtil,
uploadQueue: UploadQueue
) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, context, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue) {
init { init {
onStart() onStart()

View file

@ -30,13 +30,12 @@ import info.nightscout.androidaps.data.NonOverlappingIntervals;
import info.nightscout.androidaps.data.OverlappingIntervals; import info.nightscout.androidaps.data.OverlappingIntervals;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.ProfileIntervals; import info.nightscout.androidaps.data.ProfileIntervals;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.interfaces.ProfileStore;
import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.ProfileSwitch;
import info.nightscout.androidaps.db.Source; import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TempTarget;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.db.Treatment;
import info.nightscout.androidaps.events.EventReloadProfileSwitchData; import info.nightscout.androidaps.events.EventReloadProfileSwitchData;
import info.nightscout.androidaps.events.EventReloadTempBasalData; import info.nightscout.androidaps.events.EventReloadTempBasalData;
import info.nightscout.androidaps.events.EventReloadTreatmentData; import info.nightscout.androidaps.events.EventReloadTreatmentData;
@ -45,13 +44,15 @@ import info.nightscout.androidaps.interfaces.ActivePluginProvider;
import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginBase;
import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginDescription;
import info.nightscout.androidaps.interfaces.PluginType; import info.nightscout.androidaps.interfaces.PluginType;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.interfaces.ProfileStore;
import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.PumpInterface;
import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface;
import info.nightscout.androidaps.logging.AAPSLogger; import info.nightscout.androidaps.logging.AAPSLogger;
import info.nightscout.androidaps.logging.LTag; import info.nightscout.androidaps.logging.LTag;
import info.nightscout.androidaps.plugins.bus.RxBusWrapper; import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
import info.nightscout.androidaps.interfaces.ProfileFunction;
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload; import info.nightscout.androidaps.plugins.general.nsclient.NSUpload;
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue;
import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.general.overview.events.EventDismissNotification;
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification; import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult; import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult;
@ -75,6 +76,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
private final ProfileFunction profileFunction; private final ProfileFunction profileFunction;
private final ActivePluginProvider activePlugin; private final ActivePluginProvider activePlugin;
private final NSUpload nsUpload; private final NSUpload nsUpload;
private final UploadQueue uploadQueue;
private final FabricPrivacy fabricPrivacy; private final FabricPrivacy fabricPrivacy;
private final DateUtil dateUtil; private final DateUtil dateUtil;
@ -103,7 +105,8 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
ActivePluginProvider activePlugin, ActivePluginProvider activePlugin,
NSUpload nsUpload, NSUpload nsUpload,
FabricPrivacy fabricPrivacy, FabricPrivacy fabricPrivacy,
DateUtil dateUtil DateUtil dateUtil,
UploadQueue uploadQueue
) { ) {
super(new PluginDescription() super(new PluginDescription()
.mainType(PluginType.TREATMENT) .mainType(PluginType.TREATMENT)
@ -124,6 +127,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
this.fabricPrivacy = fabricPrivacy; this.fabricPrivacy = fabricPrivacy;
this.dateUtil = dateUtil; this.dateUtil = dateUtil;
this.nsUpload = nsUpload; this.nsUpload = nsUpload;
this.uploadQueue = uploadQueue;
} }
@Override @Override
@ -338,8 +342,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
if (last == null) { if (last == null) {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last bolus time: NOTHING FOUND"); getAapsLogger().debug(LTag.DATATREATMENTS, "Last bolus time: NOTHING FOUND");
return 0; return 0;
} } else {
else {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last bolus time: " + dateUtil.dateAndTimeString(last.date)); getAapsLogger().debug(LTag.DATATREATMENTS, "Last bolus time: " + dateUtil.dateAndTimeString(last.date));
return last.date; return last.date;
} }
@ -350,8 +353,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
if (last == null) { if (last == null) {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last manual bolus time: NOTHING FOUND"); getAapsLogger().debug(LTag.DATATREATMENTS, "Last manual bolus time: NOTHING FOUND");
return 0; return 0;
} } else {
else {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last manual bolus time: " + dateUtil.dateAndTimeString(last.date)); getAapsLogger().debug(LTag.DATATREATMENTS, "Last manual bolus time: " + dateUtil.dateAndTimeString(last.date));
return last.date; return last.date;
} }
@ -362,8 +364,7 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
if (last == null) { if (last == null) {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last Carb time: NOTHING FOUND"); getAapsLogger().debug(LTag.DATATREATMENTS, "Last Carb time: NOTHING FOUND");
return 0; return 0;
} } else {
else {
getAapsLogger().debug(LTag.DATATREATMENTS, "Last Carb time: " + dateUtil.dateAndTimeString(last.date)); getAapsLogger().debug(LTag.DATATREATMENTS, "Last Carb time: " + dateUtil.dateAndTimeString(last.date));
return last.date; return last.date;
} }
@ -387,6 +388,16 @@ public class TreatmentsPlugin extends PluginBase implements TreatmentsInterface
return getTempBasalFromHistory(System.currentTimeMillis()) != null; return getTempBasalFromHistory(System.currentTimeMillis()) != null;
} }
@Override public void removeTempBasal(TemporaryBasal tempBasal) {
String tempBasalId = tempBasal._id;
if (NSUpload.isIdValid(tempBasalId)) {
nsUpload.removeCareportalEntryFromNS(tempBasalId);
} else {
uploadQueue.removeID("dbAdd", tempBasalId);
}
MainApp.getDbHelper().delete(tempBasal);
}
@Override @Override
public boolean isInHistoryExtendedBoluslInProgress() { public boolean isInHistoryExtendedBoluslInProgress() {
return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; //TODO: crosscheck here return getExtendedBolusFromHistory(System.currentTimeMillis()) != null; //TODO: crosscheck here

View file

@ -11,7 +11,6 @@ import androidx.cardview.widget.CardView
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.android.support.DaggerFragment import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.Intervals import info.nightscout.androidaps.data.Intervals
import info.nightscout.androidaps.data.IobTotal import info.nightscout.androidaps.data.IobTotal
@ -19,33 +18,29 @@ import info.nightscout.androidaps.db.Source
import info.nightscout.androidaps.db.TemporaryBasal import info.nightscout.androidaps.db.TemporaryBasal
import info.nightscout.androidaps.events.EventTempBasalChange import info.nightscout.androidaps.events.EventTempBasalChange
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.interfaces.ProfileFunction import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder import info.nightscout.androidaps.plugins.treatments.fragments.TreatmentsTemporaryBasalsFragment.RecyclerViewAdapter.TempBasalsViewHolder
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.resources.ResourceHelper import info.nightscout.androidaps.utils.resources.ResourceHelper
import info.nightscout.androidaps.utils.sharedPreferences.SP
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.treatments_tempbasals_fragment.* import kotlinx.android.synthetic.main.treatments_tempbasals_fragment.*
import javax.inject.Inject import javax.inject.Inject
class TreatmentsTemporaryBasalsFragment : DaggerFragment() { class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
@Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var rxBus: RxBusWrapper
@Inject lateinit var sp: SP
@Inject lateinit var resourceHelper: ResourceHelper @Inject lateinit var resourceHelper: ResourceHelper
@Inject lateinit var fabricPrivacy: FabricPrivacy @Inject lateinit var fabricPrivacy: FabricPrivacy
@Inject lateinit var activePlugin: ActivePluginProvider @Inject lateinit var activePlugin: ActivePluginProvider
@Inject lateinit var profileFunction: ProfileFunction @Inject lateinit var profileFunction: ProfileFunction
@Inject lateinit var nsUpload: NSUpload
@Inject lateinit var uploadQueue: UploadQueue
@Inject lateinit var dateUtil: DateUtil @Inject lateinit var dateUtil: DateUtil
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
@ -81,6 +76,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
} }
inner class RecyclerViewAdapter internal constructor(private var tempBasalList: Intervals<TemporaryBasal>) : RecyclerView.Adapter<TempBasalsViewHolder>() { inner class RecyclerViewAdapter internal constructor(private var tempBasalList: Intervals<TemporaryBasal>) : RecyclerView.Adapter<TempBasalsViewHolder>() {
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempBasalsViewHolder { override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TempBasalsViewHolder {
val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_tempbasals_item, viewGroup, false) val v = LayoutInflater.from(viewGroup.context).inflate(R.layout.treatments_tempbasals_item, viewGroup, false)
return TempBasalsViewHolder(v) return TempBasalsViewHolder(v)
@ -142,6 +138,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
} }
inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class TempBasalsViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
var cv: CardView = itemView.findViewById(R.id.tempbasals_cardview) var cv: CardView = itemView.findViewById(R.id.tempbasals_cardview)
var date: TextView = itemView.findViewById(R.id.tempbasals_date) var date: TextView = itemView.findViewById(R.id.tempbasals_date)
var duration: TextView = itemView.findViewById(R.id.tempbasals_duration) var duration: TextView = itemView.findViewById(R.id.tempbasals_duration)
@ -166,10 +163,7 @@ class TreatmentsTemporaryBasalsFragment : DaggerFragment() {
${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.date)} ${resourceHelper.gs(R.string.date)}: ${dateUtil.dateAndTimeString(tempBasal.date)}
""".trimIndent(), """.trimIndent(),
DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
val id = tempBasal._id activePlugin.activeTreatments.removeTempBasal(tempBasal)
if (NSUpload.isIdValid(id)) nsUpload.removeCareportalEntryFromNS(id)
else uploadQueue.removeID("dbAdd", id)
MainApp.getDbHelper().delete(tempBasal)
}, null) }, null)
} }
} }

View file

@ -7,11 +7,12 @@ import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.db.TDD import info.nightscout.androidaps.db.TDD
import info.nightscout.androidaps.interfaces.ActivePluginProvider import info.nightscout.androidaps.interfaces.ActivePluginProvider
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.bus.RxBusWrapper
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.nsclient.NSUpload import info.nightscout.androidaps.plugins.general.nsclient.NSUpload
import info.nightscout.androidaps.plugins.general.nsclient.UploadQueue
import info.nightscout.androidaps.plugins.treatments.TreatmentService import info.nightscout.androidaps.plugins.treatments.TreatmentService
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
@ -34,8 +35,9 @@ class TddCalculator @Inject constructor(
val profileFunction: ProfileFunction, val profileFunction: ProfileFunction,
fabricPrivacy: FabricPrivacy, fabricPrivacy: FabricPrivacy,
nsUpload: NSUpload, nsUpload: NSUpload,
private val dateUtil: DateUtil private val dateUtil: DateUtil,
) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, mainApp, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil) { uploadQueue: UploadQueue
) : TreatmentsPlugin(injector, aapsLogger, rxBus, resourceHelper, mainApp, sp, profileFunction, activePlugin, nsUpload, fabricPrivacy, dateUtil, uploadQueue) {
init { init {
service = TreatmentService(injector) // plugin is not started service = TreatmentService(injector) // plugin is not started

View file

@ -55,6 +55,8 @@ public interface TreatmentsInterface {
NonOverlappingIntervals<TemporaryBasal> getTemporaryBasalsFromHistory(); NonOverlappingIntervals<TemporaryBasal> getTemporaryBasalsFromHistory();
void removeTempBasal(TemporaryBasal temporaryBasal);
boolean isInHistoryExtendedBoluslInProgress(); boolean isInHistoryExtendedBoluslInProgress();
ExtendedBolus getExtendedBolusFromHistory(long time); ExtendedBolus getExtendedBolusFromHistory(long time);

View file

@ -33,6 +33,7 @@ import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.data.PumpEnactResult;
import info.nightscout.androidaps.db.ExtendedBolus; import info.nightscout.androidaps.db.ExtendedBolus;
import info.nightscout.androidaps.db.Source;
import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.TemporaryBasal;
import info.nightscout.androidaps.events.EventAppExit; import info.nightscout.androidaps.events.EventAppExit;
import info.nightscout.androidaps.events.EventAppInitialized; import info.nightscout.androidaps.events.EventAppInitialized;
@ -82,6 +83,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodActiveA
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodFaultEventChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodFaultEventChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodPumpValuesChanged; 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.event.EventOmnipodTbrChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodUncertainTbrRecovered;
import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager; import info.nightscout.androidaps.plugins.pump.omnipod.manager.AapsOmnipodManager;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandAcknowledgeAlerts; import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandAcknowledgeAlerts;
import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandHandleTimeChange; import info.nightscout.androidaps.plugins.pump.omnipod.queue.command.CommandHandleTimeChange;
@ -154,6 +156,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private final Handler loopHandler = new Handler(Looper.getMainLooper()); private final Handler loopHandler = new Handler(Looper.getMainLooper());
private final Runnable statusChecker; private final Runnable statusChecker;
private boolean isSetTempBasalRunning;
private boolean isCancelTempBasalRunning; private boolean isCancelTempBasalRunning;
@Inject @Inject
@ -285,7 +288,12 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
disposables.add(rxBus disposables.add(rxBus
.toObservable(EventOmnipodTbrChanged.class) .toObservable(EventOmnipodTbrChanged.class)
.observeOn(Schedulers.io()) .observeOn(Schedulers.io())
.subscribe(event -> updateAapsTbr(), fabricPrivacy::logException) .subscribe(event -> handleCancelledTbr(), fabricPrivacy::logException)
);
disposables.add(rxBus
.toObservable(EventOmnipodUncertainTbrRecovered.class)
.observeOn(Schedulers.io())
.subscribe(event -> handleUncertainTbrRecovery(), fabricPrivacy::logException)
); );
disposables.add(rxBus disposables.add(rxBus
.toObservable(EventOmnipodActiveAlertsChanged.class) .toObservable(EventOmnipodActiveAlertsChanged.class)
@ -350,17 +358,46 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
return rileyLinkServiceData.rileyLinkServiceState.isReady(); return rileyLinkServiceData.rileyLinkServiceState.isReady();
} }
private void updateAapsTbr() { private void handleCancelledTbr() {
// As per the characteristics of the Omnipod, we only know whether or not a TBR is currently active // Only report TBR cancellations if they haven't been explicitly requested
// But it doesn't tell us the duration or amount, so we can only update TBR status in AAPS if if (isCancelTempBasalRunning) {
// The pod is not running a TBR, while AAPS thinks it is return;
if (!podStateManager.isTempBasalRunning()) { }
// Only report TBR cancellations if they haven't been explicitly requested if (!podStateManager.isTempBasalRunning() && activePlugin.getActiveTreatments().isTempBasalInProgress() && !aapsOmnipodManager.hasSuspendedFakeTbr()) {
if (!isCancelTempBasalRunning) { aapsOmnipodManager.reportCancelledTbr();
if (activePlugin.getActiveTreatments().isTempBasalInProgress() && !aapsOmnipodManager.hasSuspendedFakeTbr()) { }
aapsOmnipodManager.reportCancelledTbr(); }
}
private void handleUncertainTbrRecovery() {
// Ignore changes in certainty during tbr commands; these are normal
if (isSetTempBasalRunning || isCancelTempBasalRunning) {
return;
}
TemporaryBasal tempBasal = activePlugin.getActiveTreatments().getTempBasalFromHistory(System.currentTimeMillis());
if (podStateManager.isTempBasalRunning() && tempBasal == null) {
if (podStateManager.hasTempBasal()) {
aapsLogger.warn(LTag.PUMP, "Registering TBR that AAPS was unaware of");
long pumpId = aapsOmnipodManager.addTbrSuccessToHistory(podStateManager.getTempBasalStartTime().getMillis(),
new TempBasalPair(podStateManager.getTempBasalAmount(), false, (int) podStateManager.getTempBasalDuration().getStandardMinutes()));
TemporaryBasal temporaryBasal = new TemporaryBasal(getInjector()) //
.absolute(podStateManager.getTempBasalAmount()) //
.duration((int) podStateManager.getTempBasalDuration().getStandardMinutes())
.date(podStateManager.getTempBasalStartTime().getMillis()) //
.source(Source.PUMP) //
.pumpId(pumpId);
activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal);
} else {
// Not sure what's going on. Notify the user
aapsLogger.error(LTag.PUMP, "Unknown TBR in both Pod state and AAPS");
rxBus.send(new EventNewNotification(new Notification(Notification.OMNIPOD_PUMP_ALARM, resourceHelper.gs(R.string.omnipod_error_tbr_running_but_aaps_not_aware), Notification.NORMAL).sound(R.raw.boluserror)));
} }
} else if (!podStateManager.isTempBasalRunning() && tempBasal != null) {
aapsLogger.warn(LTag.PUMP, "Removing AAPS TBR that actually hadn't succeeded");
activePlugin.getActiveTreatments().removeTempBasal(tempBasal);
} }
} }
@ -506,18 +543,26 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
} }
/** /**
* The only actual status requests we send to the Pod here are on startup (in {@link #initializeAfterRileyLinkConnection() initializeAfterRileyLinkConnection()})
* And when the user explicitly requested it by clicking the Refresh button on the Omnipod tab (which is executed through {@link #executeCustomCommand(CustomCommand)})
* We don't do periodical status requests because that could drain the Pod's battery * We don't do periodical status requests because that could drain the Pod's battery
* The only actual status requests we send to the Pod here are on startup (in {@link #initializeAfterRileyLinkConnection() initializeAfterRileyLinkConnection()})
* And when the basal and/or temp basal status is uncertain
* When the user explicitly requested it by clicking the Refresh button on the Omnipod tab (which is executed through {@link #executeCustomCommand(CustomCommand)})
*/ */
@Override @Override
public void getPumpStatus() { public void getPumpStatus() {
if (firstRun) { if (firstRun) {
initializeAfterRileyLinkConnection(); initializeAfterRileyLinkConnection();
firstRun = false; firstRun = false;
} else if (!podStateManager.isBasalCertain() || !podStateManager.isTempBasalCertain()) {
aapsLogger.info(LTag.PUMP, "Acknowledged AAPS getPumpStatus request because basal and/or temp basal is uncertain");
getPodStatus();
} }
} }
private PumpEnactResult getPodStatus() {
return executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus);
}
@NonNull @NonNull
@Override @Override
public PumpEnactResult setNewBasalProfile(Profile profile) { public PumpEnactResult setNewBasalProfile(Profile profile) {
@ -620,7 +665,13 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
} }
} }
PumpEnactResult result = executeCommand(OmnipodCommandType.SET_TEMPORARY_BASAL, () -> aapsOmnipodManager.setTemporaryBasal(new TempBasalPair(absoluteRate, false, durationInMinutes))); isSetTempBasalRunning = true;
PumpEnactResult result;
try {
result = executeCommand(OmnipodCommandType.SET_TEMPORARY_BASAL, () -> aapsOmnipodManager.setTemporaryBasal(new TempBasalPair(absoluteRate, false, durationInMinutes)));
} finally {
isSetTempBasalRunning = false;
}
aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - setTBR. Response: " + result.success); aapsLogger.info(LTag.PUMP, "setTempBasalAbsolute - setTBR. Response: " + result.success);
@ -781,7 +832,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
case ACKNOWLEDGE_ALERTS: case ACKNOWLEDGE_ALERTS:
return executeCommand(OmnipodCommandType.ACKNOWLEDGE_ALERTS, aapsOmnipodManager::acknowledgeAlerts); return executeCommand(OmnipodCommandType.ACKNOWLEDGE_ALERTS, aapsOmnipodManager::acknowledgeAlerts);
case GET_POD_STATUS: case GET_POD_STATUS:
return executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus); return getPodStatus();
case READ_PULSE_LOG: case READ_PULSE_LOG:
return retrievePulseLog(); return retrievePulseLog();
case SUSPEND_DELIVERY: case SUSPEND_DELIVERY:
@ -857,7 +908,7 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
} else { } else {
// Even if automatically changing the time is disabled, we still want to at least do a GetStatus request, // Even if automatically changing the time is disabled, we still want to at least do a GetStatus request,
// in order to update the Pod's activation time, which we need for calculating the time on the Pod // in order to update the Pod's activation time, which we need for calculating the time on the Pod
result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus); result = getPodStatus();
} }
if (result.success) { if (result.success) {
@ -988,15 +1039,19 @@ public class OmnipodPumpPlugin extends PumpPluginBase implements PumpInterface,
private void initializeAfterRileyLinkConnection() { private void initializeAfterRileyLinkConnection() {
if (podStateManager.getActivationProgress().isAtLeast(ActivationProgress.PAIRING_COMPLETED)) { if (podStateManager.getActivationProgress().isAtLeast(ActivationProgress.PAIRING_COMPLETED)) {
boolean success = false;
for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) { for (int i = 0; STARTUP_STATUS_REQUEST_TRIES > i; i++) {
PumpEnactResult result = executeCommand(OmnipodCommandType.GET_POD_STATUS, aapsOmnipodManager::getPodStatus); PumpEnactResult result = getPodStatus();
if (result.success) { if (result.success) {
success = true;
aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup"); aapsLogger.debug(LTag.PUMP, "Successfully retrieved Pod status on startup");
break; break;
} else {
aapsLogger.warn(LTag.PUMP, "Failed to retrieve Pod status on startup");
} }
} }
if (!success) {
aapsLogger.warn(LTag.PUMP, "Failed to retrieve Pod status on startup");
rxBus.send(new EventNewNotification(new Notification(Notification.OMNIPOD_PUMP_ALARM, resourceHelper.gs(R.string.omnipod_error_failed_to_refresh_status_on_startup), Notification.NORMAL)));
}
} else { } else {
aapsLogger.debug(LTag.PUMP, "Not retrieving Pod status on startup: no Pod running"); aapsLogger.debug(LTag.PUMP, "Not retrieving Pod status on startup: no Pod running");
} }

View file

@ -171,6 +171,15 @@ public class OmnipodManager {
public synchronized void setBasalSchedule(BasalSchedule schedule, boolean acknowledgementBeep) { public synchronized void setBasalSchedule(BasalSchedule schedule, boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
if (!podStateManager.isBasalCertain()) {
try {
getPodStatus();
} catch (OmnipodException ex) {
ex.setCertainFailure(true);
throw ex;
}
}
boolean wasSuspended = podStateManager.isSuspended(); boolean wasSuspended = podStateManager.isSuspended();
if (!wasSuspended) { if (!wasSuspended) {
try { try {
@ -185,12 +194,17 @@ public class OmnipodManager {
} }
} }
BasalSchedule oldBasalSchedule = podStateManager.getBasalSchedule();
try { try {
podStateManager.setBasalSchedule(schedule);
podStateManager.setBasalCertain(false);
executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule, executeAndVerify(() -> communicationService.executeAction(new SetBasalScheduleAction(podStateManager, schedule,
false, podStateManager.getScheduleOffset(), acknowledgementBeep))); false, podStateManager.getScheduleOffset(), acknowledgementBeep)));
podStateManager.setBasalSchedule(schedule);
} catch (OmnipodException ex) { } catch (OmnipodException ex) {
if (ex.isCertainFailure()) { if (ex.isCertainFailure()) {
podStateManager.setBasalSchedule(oldBasalSchedule);
podStateManager.setBasalCertain(true);
if (!wasSuspended) { if (!wasSuspended) {
throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but setting the new basal schedule did not", ex); throw new CommandFailedAfterChangingDeliveryStatusException("Suspending delivery succeeded but setting the new basal schedule did not", ex);
} }
@ -206,6 +220,19 @@ public class OmnipodManager {
public synchronized void setTemporaryBasal(double rate, Duration duration, boolean acknowledgementBeep, boolean completionBeep) { public synchronized void setTemporaryBasal(double rate, Duration duration, boolean acknowledgementBeep, boolean completionBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
if (!podStateManager.isTempBasalCertain() || !podStateManager.isBasalCertain()) {
try {
getPodStatus();
} catch (OmnipodException ex) {
ex.setCertainFailure(true);
throw ex;
}
}
if (podStateManager.isSuspended()) {
throw new IllegalDeliveryStatusException(DeliveryStatus.NORMAL, DeliveryStatus.SUSPENDED);
}
boolean cancelCurrentTbr = podStateManager.isTempBasalRunning(); boolean cancelCurrentTbr = podStateManager.isTempBasalRunning();
if (cancelCurrentTbr) { if (cancelCurrentTbr) {
@ -217,17 +244,19 @@ public class OmnipodManager {
} }
// Uncertain failure // Uncertain failure
podStateManager.setTempBasalCertain(false);
throw new PrecedingCommandFailedUncertainlyException(ex); throw new PrecedingCommandFailedUncertainlyException(ex);
} }
} }
try { try {
podStateManager.setTempBasal(DateTime.now().plus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration);
podStateManager.setTempBasalCertain(false);
executeAndVerify(() -> communicationService.executeAction(new SetTempBasalAction( executeAndVerify(() -> communicationService.executeAction(new SetTempBasalAction(
podStateManager, rate, duration, acknowledgementBeep, completionBeep))); podStateManager, rate, duration, acknowledgementBeep, completionBeep)));
podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, true);
} catch (OmnipodException ex) { } catch (OmnipodException ex) {
if (ex.isCertainFailure()) { if (ex.isCertainFailure()) {
podStateManager.clearTempBasal();
podStateManager.setTempBasalCertain(true);
if (cancelCurrentTbr) { if (cancelCurrentTbr) {
throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex); throw new CommandFailedAfterChangingDeliveryStatusException("Failed to set new TBR while cancelling old TBR succeeded", ex);
} }
@ -235,7 +264,6 @@ public class OmnipodManager {
} }
// Uncertain failure // Uncertain failure
podStateManager.setTempBasal(DateTime.now().minus(OmnipodConstants.AVERAGE_TEMP_BASAL_COMMAND_COMMUNICATION_DURATION), rate, duration, false);
throw ex; throw ex;
} }
} }
@ -247,11 +275,33 @@ public class OmnipodManager {
private synchronized StatusResponse cancelDelivery(EnumSet<DeliveryType> deliveryTypes, boolean acknowledgementBeep) { private synchronized StatusResponse cancelDelivery(EnumSet<DeliveryType> deliveryTypes, boolean acknowledgementBeep) {
assertReadyForDelivery(); assertReadyForDelivery();
return executeAndVerify(() -> { if (deliveryTypes.contains(DeliveryType.BASAL)) {
StatusResponse statusResponse = communicationService.executeAction(new CancelDeliveryAction(podStateManager, deliveryTypes, acknowledgementBeep)); podStateManager.setBasalCertain(false);
aapsLogger.info(LTag.PUMPCOMM, "Status response after cancel delivery[types={}]: {}", deliveryTypes.toString(), statusResponse.toString()); }
return statusResponse; if (deliveryTypes.contains(DeliveryType.TEMP_BASAL)) {
}); podStateManager.setTempBasalCertain(false);
}
try {
return executeAndVerify(() -> {
StatusResponse 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;
});
} catch (OmnipodException ex) {
if (ex.isCertainFailure()) {
if (deliveryTypes.contains(DeliveryType.BASAL)) {
podStateManager.setBasalCertain(true);
}
if (deliveryTypes.contains(DeliveryType.TEMP_BASAL)) {
podStateManager.setTempBasalCertain(true);
}
}
throw ex;
}
} }
// Returns a SingleSubject that returns when the bolus has finished. // Returns a SingleSubject that returns when the bolus has finished.
@ -260,6 +310,19 @@ public class OmnipodManager {
public synchronized BolusCommandResult bolus(Double units, boolean acknowledgementBeep, boolean completionBeep, BiConsumer<Double, Integer> progressIndicationConsumer) { public synchronized BolusCommandResult bolus(Double units, boolean acknowledgementBeep, boolean completionBeep, BiConsumer<Double, Integer> progressIndicationConsumer) {
assertReadyForDelivery(); assertReadyForDelivery();
if (!podStateManager.isBasalCertain()) {
try {
getPodStatus();
} catch (OmnipodException ex) {
ex.setCertainFailure(true);
throw ex;
}
}
if (podStateManager.isSuspended()) {
throw new IllegalDeliveryStatusException(DeliveryStatus.NORMAL, DeliveryStatus.SUSPENDED);
}
bolusCommandExecutionSubject = SingleSubject.create(); bolusCommandExecutionSubject = SingleSubject.create();
CommandDeliveryStatus commandDeliveryStatus = CommandDeliveryStatus.SUCCESS; CommandDeliveryStatus commandDeliveryStatus = CommandDeliveryStatus.SUCCESS;

View file

@ -368,6 +368,15 @@ public abstract class PodStateManager {
setAndStore(() -> podState.setBasalSchedule(basalSchedule)); setAndStore(() -> podState.setBasalSchedule(basalSchedule));
} }
public final boolean isBasalCertain() {
Boolean certain = getSafe(() -> podState.isBasalCertain());
return certain == null || certain;
}
public final void setBasalCertain(boolean certain) {
setAndStore(() -> podState.setBasalCertain(certain));
}
public final DateTime getLastBolusStartTime() { public final DateTime getLastBolusStartTime() {
return getSafe(() -> podState.getLastBolusStartTime()); return getSafe(() -> podState.getLastBolusStartTime());
} }
@ -416,14 +425,19 @@ public abstract class PodStateManager {
} }
public final void setTempBasalCertain(boolean certain) { public final void setTempBasalCertain(boolean certain) {
setSafe(() -> podState.setTempBasalCertain(certain)); setAndStore(() -> {
if (!Objects.equals(podState.isTempBasalCertain(), certain)) {
podState.setTempBasalCertain(certain);
onTbrChanged();
}
});
} }
public final void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean certain) { public final void setTempBasal(DateTime startTime, Double amount, Duration duration) {
setTempBasal(startTime, amount, duration, certain, true); setTempBasal(startTime, amount, duration, true);
} }
public final void setTempBasal(DateTime startTime, Double amount, Duration duration, Boolean certain, boolean store) { private void setTempBasal(DateTime startTime, Double amount, Duration duration, boolean store) {
DateTime currentStartTime = getTempBasalStartTime(); DateTime currentStartTime = getTempBasalStartTime();
Double currentAmount = getTempBasalAmount(); Double currentAmount = getTempBasalAmount();
Duration currentDuration = getTempBasalDuration(); Duration currentDuration = getTempBasalDuration();
@ -432,7 +446,6 @@ public abstract class PodStateManager {
podState.setTempBasalStartTime(startTime); podState.setTempBasalStartTime(startTime);
podState.setTempBasalAmount(amount); podState.setTempBasalAmount(amount);
podState.setTempBasalDuration(duration); podState.setTempBasalDuration(duration);
podState.setTempBasalCertain(certain);
}; };
if (store) { if (store) {
@ -444,6 +457,14 @@ public abstract class PodStateManager {
} }
} }
public final void clearTempBasal() {
clearTempBasal(true);
}
private void clearTempBasal(boolean store) {
setTempBasal(null, null, null, store);
}
/** /**
* @return true when a Temp Basal is stored in the Pod Stated * @return true when a Temp Basal is stored in the Pod Stated
* Please note that this could also be an expired Temp Basal. For an indication on whether or not * Please note that this could also be an expired Temp Basal. For an indication on whether or not
@ -457,13 +478,22 @@ public abstract class PodStateManager {
* @return true when a Temp Basal is stored in the Pod State and this temp basal is currently running (based on start time and duration) * @return true when a Temp Basal is stored in the Pod State and this temp basal is currently running (based on start time and duration)
*/ */
public final boolean isTempBasalRunning() { public final boolean isTempBasalRunning() {
return isTempBasalRunningAt(DateTime.now()); return isTempBasalRunningAt(null);
} }
/** /**
* @return true when a Temp Basal is stored in the Pod State and this temp basal is running at the given time (based on start time and duration) * @param time the time for which to look up whether a temp basal is running, null meaning now
* @return true when a Temp Basal is stored in the Pod State and this temp basal is running at the given time (based on start time and duration),
* or when the time provided is null and the delivery status of the Pod inidicated that a TBR is running, but not TBR is stored
* This can happen in some rare cases.
*/ */
public final boolean isTempBasalRunningAt(DateTime time) { public final boolean isTempBasalRunningAt(DateTime time) {
if (time == null) { // now
if (!hasTempBasal() && getLastDeliveryStatus().isTbrRunning()) {
return true;
}
time = DateTime.now();
}
if (hasTempBasal()) { if (hasTempBasal()) {
DateTime tempBasalStartTime = getTempBasalStartTime(); DateTime tempBasalStartTime = getTempBasalStartTime();
DateTime tempBasalEndTime = tempBasalStartTime.plus(getTempBasalDuration()); DateTime tempBasalEndTime = tempBasalStartTime.plus(getTempBasalDuration());
@ -537,15 +567,26 @@ public abstract class PodStateManager {
podState.setTotalTicksDelivered(status.getTicksDelivered()); podState.setTotalTicksDelivered(status.getTicksDelivered());
podState.setPodProgressStatus(status.getPodProgressStatus()); podState.setPodProgressStatus(status.getPodProgressStatus());
podState.setTimeActive(status.getTimeActive()); podState.setTimeActive(status.getTimeActive());
if (status.getDeliveryStatus().isTbrRunning()) {
if (!isTempBasalCertain() && isTempBasalRunning()) { boolean isBasalCertain = podState.isBasalCertain() == null || podState.isBasalCertain();
podState.setTempBasalCertain(true); boolean isTempBasalCertain = podState.isTempBasalCertain() == null || podState.isTempBasalCertain();
if (!status.getDeliveryStatus().isTbrRunning()) {
if (isTempBasalCertain) {
clearTempBasal(); // Triggers onTbrChanged when appropriate
} else {
// Don't trigger onTbrChanged as we will trigger onUncertainTbrRecovered below
podState.setTempBasalStartTime(null);
podState.setTempBasalAmount(null);
podState.setTempBasalDuration(null);
} }
} else {
// Triggers {@link #onTbrChanged() onTbrChanged()} when appropriate
setTempBasal(null, null, null, true, false);
} }
podState.setLastUpdatedFromResponse(DateTime.now()); if (!isTempBasalCertain) {
podState.setTempBasalCertain(true);
onUncertainTbrRecovered();
}
if (!isBasalCertain) {
podState.setBasalCertain(true);
}
if (status instanceof PodInfoDetailedStatus) { if (status instanceof PodInfoDetailedStatus) {
PodInfoDetailedStatus detailedStatus = (PodInfoDetailedStatus) status; PodInfoDetailedStatus detailedStatus = (PodInfoDetailedStatus) status;
@ -556,6 +597,8 @@ public abstract class PodStateManager {
} }
} }
} }
podState.setLastUpdatedFromResponse(DateTime.now());
}); });
} }
@ -564,6 +607,11 @@ public abstract class PodStateManager {
// Can be overridden in subclasses // Can be overridden in subclasses
} }
protected void onUncertainTbrRecovered() {
// Deliberately left empty
// Can be overridden in subclasses
}
protected void onActiveAlertsChanged() { protected void onActiveAlertsChanged() {
// Deliberately left empty // Deliberately left empty
// Can be overridden in subclasses // Can be overridden in subclasses
@ -667,6 +715,7 @@ public abstract class PodStateManager {
private DeliveryStatus lastDeliveryStatus; private DeliveryStatus lastDeliveryStatus;
private AlertSet activeAlerts; private AlertSet activeAlerts;
private BasalSchedule basalSchedule; private BasalSchedule basalSchedule;
private Boolean basalCertain;
private DateTime lastBolusStartTime; private DateTime lastBolusStartTime;
private Double lastBolusAmount; private Double lastBolusAmount;
private Duration lastBolusDuration; private Duration lastBolusDuration;
@ -871,6 +920,14 @@ public abstract class PodStateManager {
this.basalSchedule = basalSchedule; this.basalSchedule = basalSchedule;
} }
Boolean isBasalCertain() {
return basalCertain;
}
void setBasalCertain(Boolean certain) {
this.basalCertain = certain;
}
DateTime getLastBolusStartTime() { DateTime getLastBolusStartTime() {
return lastBolusStartTime; return lastBolusStartTime;
} }

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 EventOmnipodUncertainTbrRecovered : Event()

View file

@ -1,5 +1,8 @@
package info.nightscout.androidaps.plugins.pump.omnipod.manager; package info.nightscout.androidaps.plugins.pump.omnipod.manager;
import android.content.Context;
import android.content.Intent;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.joda.time.Duration; import org.joda.time.Duration;
import org.json.JSONException; import org.json.JSONException;
@ -14,6 +17,7 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.android.HasAndroidInjector; import dagger.android.HasAndroidInjector;
import info.nightscout.androidaps.activities.ErrorHelperActivity;
import info.nightscout.androidaps.data.DetailedBolusInfo; import info.nightscout.androidaps.data.DetailedBolusInfo;
import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.Profile;
import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.data.PumpEnactResult;
@ -102,6 +106,7 @@ public class AapsOmnipodManager {
private final OmnipodAlertUtil omnipodAlertUtil; private final OmnipodAlertUtil omnipodAlertUtil;
private final NSUpload nsUpload; private final NSUpload nsUpload;
private final ProfileFunction profileFunction; private final ProfileFunction profileFunction;
private final Context context;
private boolean basalBeepsEnabled; private boolean basalBeepsEnabled;
private boolean bolusBeepsEnabled; private boolean bolusBeepsEnabled;
@ -128,8 +133,9 @@ public class AapsOmnipodManager {
DatabaseHelperInterface databaseHelper, DatabaseHelperInterface databaseHelper,
OmnipodAlertUtil omnipodAlertUtil, OmnipodAlertUtil omnipodAlertUtil,
NSUpload nsUpload, NSUpload nsUpload,
ProfileFunction profileFunction ProfileFunction profileFunction,
) { Context context) {
this.podStateManager = podStateManager; this.podStateManager = podStateManager;
this.aapsOmnipodUtil = aapsOmnipodUtil; this.aapsOmnipodUtil = aapsOmnipodUtil;
this.aapsLogger = aapsLogger; this.aapsLogger = aapsLogger;
@ -142,6 +148,7 @@ public class AapsOmnipodManager {
this.omnipodAlertUtil = omnipodAlertUtil; this.omnipodAlertUtil = omnipodAlertUtil;
this.nsUpload = nsUpload; this.nsUpload = nsUpload;
this.profileFunction = profileFunction; this.profileFunction = profileFunction;
this.context = context;
delegate = new OmnipodManager(aapsLogger, communicationService, podStateManager); delegate = new OmnipodManager(aapsLogger, communicationService, podStateManager);
@ -357,7 +364,7 @@ public class AapsOmnipodManager {
if (detailedBolusInfo.isSMB) { if (detailedBolusInfo.isSMB) {
showNotification(getStringResource(R.string.omnipod_error_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, isNotificationUncertainSmbSoundEnabled() ? R.raw.boluserror : null); showNotification(getStringResource(R.string.omnipod_error_bolus_failed_uncertain_smb, detailedBolusInfo.insulin), Notification.URGENT, isNotificationUncertainSmbSoundEnabled() ? R.raw.boluserror : null);
} else { } else {
showNotification(getStringResource(R.string.omnipod_error_bolus_failed_uncertain), Notification.URGENT, isNotificationUncertainBolusSoundEnabled() ? R.raw.boluserror : null); showErrorDialog(getStringResource(R.string.omnipod_error_bolus_failed_uncertain), isNotificationUncertainBolusSoundEnabled() ? R.raw.boluserror : null);
} }
} }
@ -710,6 +717,10 @@ public class AapsOmnipodManager {
activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal); activePlugin.getActiveTreatments().addToHistoryTempBasal(temporaryBasal);
} }
public long addTbrSuccessToHistory(long requestTime, TempBasalPair tempBasalPair) {
return addSuccessToHistory(requestTime, PodHistoryEntryType.SET_TEMPORARY_BASAL, tempBasalPair);
}
private void addTempBasalTreatment(long time, long pumpId, TempBasalPair tempBasalPair) { private void addTempBasalTreatment(long time, long pumpId, TempBasalPair tempBasalPair) {
TemporaryBasal tempStart = new TemporaryBasal(injector) // TemporaryBasal tempStart = new TemporaryBasal(injector) //
.date(time) // .date(time) //
@ -850,6 +861,15 @@ public class AapsOmnipodManager {
rxBus.send(event); rxBus.send(event);
} }
private void showErrorDialog(String message, Integer sound) {
Intent intent = new Intent(context, ErrorHelperActivity.class);
intent.putExtra("soundid", sound);
intent.putExtra("status", message);
intent.putExtra("title", resourceHelper.gs(R.string.error));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
private void showPodFaultNotification(FaultEventCode faultEventCode) { private void showPodFaultNotification(FaultEventCode faultEventCode) {
showPodFaultNotification(faultEventCode, R.raw.boluserror); showPodFaultNotification(faultEventCode, R.raw.boluserror);
} }

View file

@ -10,6 +10,7 @@ import info.nightscout.androidaps.plugins.pump.omnipod.driver.manager.PodStateMa
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodActiveAlertsChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodActiveAlertsChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodFaultEventChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodFaultEventChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged; import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodTbrChanged;
import info.nightscout.androidaps.plugins.pump.omnipod.event.EventOmnipodUncertainTbrRecovered;
import info.nightscout.androidaps.utils.sharedPreferences.SP; import info.nightscout.androidaps.utils.sharedPreferences.SP;
@Singleton @Singleton
@ -34,6 +35,10 @@ public class AapsPodStateManager extends PodStateManager {
sp.putString(OmnipodStorageKeys.Preferences.POD_STATE, podState); sp.putString(OmnipodStorageKeys.Preferences.POD_STATE, podState);
} }
@Override protected void onUncertainTbrRecovered() {
rxBus.send(new EventOmnipodUncertainTbrRecovered());
}
@Override protected void onTbrChanged() { @Override protected void onTbrChanged() {
rxBus.send(new EventOmnipodTbrChanged()); rxBus.send(new EventOmnipodTbrChanged());
} }

View file

@ -361,11 +361,17 @@ class OmnipodOverviewFragment : DaggerFragment() {
} }
} else { } else {
if (podStateManager.podProgressStatus.isRunning) { if (podStateManager.podProgressStatus.isRunning) {
if (podStateManager.isSuspended) { var status = if (podStateManager.isSuspended) {
resourceHelper.gs(R.string.omnipod_pod_status_suspended) resourceHelper.gs(R.string.omnipod_pod_status_suspended)
} else { } else {
resourceHelper.gs(R.string.omnipod_pod_status_running) resourceHelper.gs(R.string.omnipod_pod_status_running)
} }
if (!podStateManager.isBasalCertain) {
status += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")"
}
status
} else if (podStateManager.podProgressStatus == PodProgressStatus.FAULT_EVENT_OCCURRED) { } else if (podStateManager.podProgressStatus == PodProgressStatus.FAULT_EVENT_OCCURRED) {
resourceHelper.gs(R.string.omnipod_pod_status_pod_fault) resourceHelper.gs(R.string.omnipod_pod_status_pod_fault)
} else if (podStateManager.podProgressStatus == PodProgressStatus.INACTIVE) { } else if (podStateManager.podProgressStatus == PodProgressStatus.INACTIVE) {
@ -375,7 +381,7 @@ class OmnipodOverviewFragment : DaggerFragment() {
} }
} }
val podStatusColor = if (!podStateManager.isPodActivationCompleted || podStateManager.isPodDead || podStateManager.isSuspended) { val podStatusColor = if (!podStateManager.isPodActivationCompleted || podStateManager.isPodDead || podStateManager.isSuspended || (podStateManager.isPodRunning && !podStateManager.isBasalCertain)) {
Color.RED Color.RED
} else { } else {
Color.WHITE Color.WHITE
@ -406,18 +412,36 @@ class OmnipodOverviewFragment : DaggerFragment() {
private fun updateTempBasal() { private fun updateTempBasal() {
if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) { if (podStateManager.isPodActivationCompleted && podStateManager.isTempBasalRunning) {
val now = DateTime.now() if (!podStateManager.hasTempBasal()) {
omnipod_overview_temp_basal.text = "???"
omnipod_overview_temp_basal.setTextColor(Color.RED)
} else {
val now = DateTime.now()
val startTime = podStateManager.tempBasalStartTime val startTime = podStateManager.tempBasalStartTime
val amount = podStateManager.tempBasalAmount val amount = podStateManager.tempBasalAmount
val duration = podStateManager.tempBasalDuration val duration = podStateManager.tempBasalDuration
val minutesRunning = Duration(startTime, now).standardMinutes val minutesRunning = Duration(startTime, now).standardMinutes
var text: String var text: String
val textColor: Int
text = resourceHelper.gs(R.string.omnipod_overview_temp_basal_value, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes)
if (podStateManager.isTempBasalCertain) {
textColor = Color.WHITE
} else {
textColor = Color.RED
text += " (" + resourceHelper.gs(R.string.omnipod_uncertain) + ")"
}
omnipod_overview_temp_basal.text = text
omnipod_overview_temp_basal.setTextColor(textColor)
}
} else {
var text = PLACEHOLDER
val textColor: Int val textColor: Int
text = resourceHelper.gs(R.string.omnipod_overview_temp_basal_value, amount, dateUtil.timeString(startTime.millis), minutesRunning, duration.standardMinutes)
if (podStateManager.isTempBasalCertain) { if (!podStateManager.isPodActivationCompleted || podStateManager.isTempBasalCertain) {
textColor = Color.WHITE textColor = Color.WHITE
} else { } else {
textColor = Color.RED textColor = Color.RED
@ -426,9 +450,6 @@ class OmnipodOverviewFragment : DaggerFragment() {
omnipod_overview_temp_basal.text = text omnipod_overview_temp_basal.text = text
omnipod_overview_temp_basal.setTextColor(textColor) omnipod_overview_temp_basal.setTextColor(textColor)
} else {
omnipod_overview_temp_basal.text = PLACEHOLDER
omnipod_overview_temp_basal.setTextColor(Color.WHITE)
} }
} }

View file

@ -122,6 +122,7 @@
<string name="omnipod_error_unknown_custom_command">Unknown custom command: %1$s</string> <string name="omnipod_error_unknown_custom_command">Unknown custom command: %1$s</string>
<string name="omnipod_error_failed_to_read_pulse_log">Failed to read Pulse Log</string> <string name="omnipod_error_failed_to_read_pulse_log">Failed to read Pulse Log</string>
<string name="omnipod_error_failed_to_refresh_status">Failed to refresh status</string> <string name="omnipod_error_failed_to_refresh_status">Failed to refresh status</string>
<string name="omnipod_error_failed_to_refresh_status_on_startup">Failed to refresh status on startup</string>
<string name="omnipod_error_failed_to_acknowledge_alerts">Failed to acknowledge alerts</string> <string name="omnipod_error_failed_to_acknowledge_alerts">Failed to acknowledge alerts</string>
<string name="omnipod_error_failed_to_suspend_delivery">Failed to suspend delivery</string> <string name="omnipod_error_failed_to_suspend_delivery">Failed to suspend delivery</string>
<string name="omnipod_error_failed_to_set_time">Failed to set time</string> <string name="omnipod_error_failed_to_set_time">Failed to set time</string>
@ -135,6 +136,7 @@
<string name="omnipod_error_pod_fault_activation_time_exceeded">The Pod\'s activation time has been exceeded. This Pod can no longer be activated.</string> <string name="omnipod_error_pod_fault_activation_time_exceeded">The Pod\'s activation time has been exceeded. This Pod can no longer be activated.</string>
<string name="omnipod_error_failed_to_verify_activation_progress">Failed to verify activation progress. Please retry.</string> <string name="omnipod_error_failed_to_verify_activation_progress">Failed to verify activation progress. Please retry.</string>
<string name="omnipod_error_pod_suspended">Pod suspended</string> <string name="omnipod_error_pod_suspended">Pod suspended</string>
<string name="omnipod_error_tbr_running_but_aaps_not_aware">A temporary basal is running on the Pod, but AAPS is unaware of this temporary basal. Please cancel your temporary basal manually.</string>
<!-- Omnipod - Confirmation --> <!-- Omnipod - Confirmation -->
<string name="omnipod_confirmation">Confirmation</string> <string name="omnipod_confirmation">Confirmation</string>