From 4ca9d220628021d6036cd69368ee716e9273e0ba Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 4 Sep 2018 19:55:31 +0200 Subject: [PATCH 1/6] Virtual pump descriptions used for APS requests in open loop mode --- .../androidaps/db/TemporaryBasal.java | 60 ++++--- .../ConstraintsSafety/SafetyPlugin.java | 8 +- .../androidaps/plugins/Loop/APSResult.java | 170 +++++++++++++++++- .../androidaps/plugins/Loop/LoopPlugin.java | 106 +++++++---- .../OpenAPSAMA/DetermineBasalResultAMA.java | 13 +- .../plugins/OpenAPSAMA/OpenAPSAMAPlugin.java | 11 -- .../OpenAPSMA/DetermineBasalResultMA.java | 14 +- .../plugins/OpenAPSMA/OpenAPSMAPlugin.java | 10 -- .../OpenAPSSMB/DetermineBasalResultSMB.java | 18 +- .../plugins/OpenAPSSMB/OpenAPSSMBPlugin.java | 10 -- app/src/test/java/info/AAPSMocker.java | 24 ++- .../interfaces/ConstraintsCheckerTest.java | 4 +- .../Dialogs/NewNSTreatmentDialogTest.java | 2 +- .../ConstraintsSafety/SafetyPluginTest.java | 4 +- .../plugins/Loop/APSREsultTest.java | 168 +++++++++++++++++ 15 files changed, 473 insertions(+), 149 deletions(-) create mode 100644 app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java diff --git a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java index 7fdc7ac117..09331a52ce 100644 --- a/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java +++ b/app/src/main/java/info/nightscout/androidaps/db/TemporaryBasal.java @@ -221,7 +221,7 @@ public class TemporaryBasal implements Interval { public IobTotal iobCalc(long time, Profile profile) { - if(isFakeExtended){ + if (isFakeExtended) { log.error("iobCalc should only be called on Extended boluses separately"); return new IobTotal(time); } @@ -229,7 +229,7 @@ public class TemporaryBasal implements Interval { IobTotal result = new IobTotal(time); InsulinInterface insulinInterface = ConfigBuilderPlugin.getActiveInsulin(); - int realDuration = getDurationToTime(time); + int realDuration = getDurationToTime(time); double netBasalAmount = 0d; if (realDuration > 0) { @@ -292,12 +292,22 @@ public class TemporaryBasal implements Interval { } public double tempBasalConvertedToAbsolute(long time, Profile profile) { - if(isFakeExtended){ + if (isFakeExtended) { return profile.getBasal(time) + netExtendedRate; } else if (isAbsolute) { return absoluteRate; } else { - return profile.getBasal(time) * percentRate / 100; + return profile.getBasal(time) * percentRate / 100; + } + } + + public int tempBasalConvertedToPercent(long time, Profile profile) { + if (isFakeExtended) { + return (int) ((profile.getBasal(time) + netExtendedRate) / profile.getBasal(time)) * 100; + } else if (isAbsolute) { + return (int) (absoluteRate / profile.getBasal(time)) * 100; + } else { + return percentRate; } } @@ -318,12 +328,12 @@ public class TemporaryBasal implements Interval { } public String toStringFull() { - if(isFakeExtended){ + if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); Double currentBasalRate = profile.getBasal(); - double rate = (currentBasalRate == null)?0d:(currentBasalRate+netExtendedRate); - return getCalcuatedPercentageIfNeeded() + DecimalFormatter.to2Decimal(rate) + "U/h ("+DecimalFormatter.to2Decimal(netExtendedRate)+"E) @" + + double rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); + return getCalcuatedPercentageIfNeeded() + DecimalFormatter.to2Decimal(rate) + "U/h (" + DecimalFormatter.to2Decimal(netExtendedRate) + "E) @" + DateUtil.timeString(date) + " " + getRealDuration() + "/" + durationInMinutes + "'"; } else if (isAbsolute) { @@ -340,21 +350,21 @@ public class TemporaryBasal implements Interval { public String toStringShort() { if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate = 0d; if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null)?0d:(currentBasalRate+netExtendedRate); - } else if (isAbsolute){ + rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); + } else if (isAbsolute) { rate = absoluteRate; } - if(SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false) && SP.getBoolean(R.string.key_danar_useextended, false)){ + if (SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false) && SP.getBoolean(R.string.key_danar_useextended, false)) { Profile profile = ProfileFunctions.getInstance().getProfile(); - if(profile != null) { + if (profile != null) { double basal = profile.getBasal(); - if(basal != 0){ - return Math.round(rate*100d/basal) + "%"; + if (basal != 0) { + return Math.round(rate * 100d / basal) + "%"; } } } @@ -364,24 +374,24 @@ public class TemporaryBasal implements Interval { } } - private String getCalcuatedPercentageIfNeeded(){ + private String getCalcuatedPercentageIfNeeded() { if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate = 0d; if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null)?0d:(currentBasalRate+netExtendedRate); - } else if (isAbsolute){ + rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); + } else if (isAbsolute) { rate = absoluteRate; } - if(SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false) && SP.getBoolean(R.string.key_danar_useextended, false)){ + if (SP.getBoolean(R.string.key_danar_visualizeextendedaspercentage, false) && SP.getBoolean(R.string.key_danar_useextended, false)) { Profile profile = ProfileFunctions.getInstance().getProfile(); - if(profile != null) { + if (profile != null) { double basal = profile.getBasal(); - if(basal != 0){ - return Math.round(rate*100d/basal) + "% "; + if (basal != 0) { + return Math.round(rate * 100d / basal) + "% "; } } } @@ -392,12 +402,12 @@ public class TemporaryBasal implements Interval { public String toStringVeryShort() { if (isAbsolute || isFakeExtended) { - double rate = 0d; + double rate = 0d; if (isFakeExtended) { Profile profile = ProfileFunctions.getInstance().getProfile(); Double currentBasalRate = profile.getBasal(); - rate = (currentBasalRate == null)?0d:(currentBasalRate+netExtendedRate); - } else if (isAbsolute){ + rate = (currentBasalRate == null) ? 0d : (currentBasalRate + netExtendedRate); + } else if (isAbsolute) { rate = absoluteRate; } return DecimalFormatter.to2Decimal(rate) + "U/h "; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java index 9dd6665b4d..59f44c77c7 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPlugin.java @@ -126,7 +126,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); // check for pump max - if (pump != null) { + if (pump != null && pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) { double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose(); absoluteRate.setIfSmaller(pumpLimit, String.format(MainApp.gs(R.string.limitingbasalratio), pumpLimit, MainApp.gs(R.string.pumplimit)), this); } @@ -151,6 +151,7 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { percentRate.copyReasons(absoluteConstraint); PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + Integer percentRateAfterConst = Double.valueOf(absoluteConstraint.value() / currentBasal * 100).intValue(); if (pump != null) { if (percentRateAfterConst < 100) @@ -161,6 +162,11 @@ public class SafetyPlugin extends PluginBase implements ConstraintsInterface { percentRate.set(percentRateAfterConst, String.format(MainApp.gs(R.string.limitingpercentrate), percentRateAfterConst, MainApp.gs(R.string.pumplimit)), this); + if (pump != null && pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) { + double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose(); + percentRate.setIfSmaller((int) pumpLimit, String.format(MainApp.gs(R.string.limitingbasalratio), pumpLimit, MainApp.gs(R.string.pumplimit)), this); + } + return percentRate; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 8ea19fe8a7..374bfbcd1d 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -15,11 +15,16 @@ import java.util.List; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.IobTotal; +import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.utils.DecimalFormatter; /** @@ -31,6 +36,8 @@ public class APSResult { public long date = 0; public String reason; public double rate; + public int percent; + public boolean usePercent = false; public int duration; public boolean tempBasalRequested = false; public boolean bolusRequested = false; @@ -43,8 +50,37 @@ public class APSResult { public Constraint inputConstraints; public Constraint rateConstraint; + public Constraint percentConstraint; public Constraint smbConstraint; + public APSResult rate(double rate) { + this.rate = rate; + return this; + } + + public APSResult duration(int duration) { + this.duration = duration; + return this; + } + + public APSResult percent(int percent) { + this.percent = percent; + return this; + } + + + public APSResult tempBasalRequested(boolean tempBasalRequested) { + this.tempBasalRequested = tempBasalRequested; + return this; + } + + + public APSResult usePercent(boolean usePercent) { + this.usePercent = usePercent; + return this; + } + + @Override public String toString() { final PumpInterface pump = ConfigBuilderPlugin.getActivePump(); @@ -55,6 +91,10 @@ public class APSResult { ret = MainApp.gs(R.string.canceltemp) + "\n"; else if (rate == -1) ret = MainApp.gs(R.string.let_temp_basal_run) + "\n"; + else if (usePercent) + ret = MainApp.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(percent) + "% " + + "(" + DecimalFormatter.to2Decimal(percent * pump.getBaseBasalRate() / 100d) + " U/h)\n" + + MainApp.gs(R.string.duration) + ": " + DecimalFormatter.to2Decimal(duration) + " min\n"; else ret = MainApp.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + "(" + DecimalFormatter.to2Decimal(rate / pump.getBaseBasalRate() * 100) + "%) \n" + @@ -80,6 +120,10 @@ public class APSResult { ret = MainApp.gs(R.string.canceltemp) + "
"; else if (rate == -1) ret = MainApp.gs(R.string.let_temp_basal_run) + "
"; + else if (usePercent) + ret = "" + MainApp.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(percent) + "% " + + "(" + DecimalFormatter.to2Decimal(percent * pump.getBaseBasalRate() / 100d) + " U/h)
" + + "" + MainApp.gs(R.string.duration) + ": " + DecimalFormatter.to2Decimal(duration) + " min
"; else ret = "" + MainApp.gs(R.string.rate) + ": " + DecimalFormatter.to2Decimal(rate) + " U/h " + "(" + DecimalFormatter.to2Decimal(rate / pump.getBaseBasalRate() * 100) + "%)
" + @@ -101,21 +145,33 @@ public class APSResult { public APSResult clone() { APSResult newResult = new APSResult(); - newResult.reason = reason; + doClone(newResult); + return newResult; + } + + protected void doClone(APSResult newResult) { + newResult.date = date; + newResult.reason = new String(reason); newResult.rate = rate; newResult.duration = duration; newResult.tempBasalRequested = tempBasalRequested; newResult.bolusRequested = bolusRequested; newResult.iob = iob; - newResult.json = json; + try { + newResult.json = new JSONObject(json.toString()); + } catch (JSONException e) { + log.error("Unhandled exception", e); + } newResult.hasPredictions = hasPredictions; newResult.smb = smb; newResult.deliverAt = deliverAt; newResult.rateConstraint = rateConstraint; newResult.smbConstraint = smbConstraint; - return newResult; + newResult.percent = percent; + newResult.usePercent = usePercent; } + public JSONObject json() { JSONObject json = new JSONObject(); try { @@ -228,6 +284,112 @@ public class APSResult { } public boolean isChangeRequested() { - return tempBasalRequested || bolusRequested; + Constraint closedLoopEnabled = MainApp.getConstraintChecker().isClosedLoopAllowed(); + // closed loop mode: handle change at driver level + if (closedLoopEnabled.value()) { + if (L.isEnabled(L.APS)) + log.debug("DEFAULT: Closed mode"); + return tempBasalRequested || bolusRequested; + } + + // open loop mode: try to limit request + if (!tempBasalRequested && !bolusRequested) { + if (L.isEnabled(L.APS)) + log.debug("FALSE: No request"); + return false; + } + + long now = System.currentTimeMillis(); + TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); + PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); + Profile profile = ProfileFunctions.getInstance().getProfile(); + + if (usePercent) { + if (activeTemp == null && percent == 100) { + if (L.isEnabled(L.APS)) + log.debug("FALSE: No temp running, asking cancel temp"); + return false; + } + if (activeTemp != null && Math.abs(percent - activeTemp.tempBasalConvertedToPercent(now, profile)) < pump.getPumpDescription().basalStep) { + if (L.isEnabled(L.APS)) + log.debug("FALSE: Temp equal"); + return false; + } + // always report zerotemp + if (percent == 0) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Zero temp"); + return true; + } + // always report hightemp + if (pump != null && pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) { + double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose(); + if (percent == pumpLimit) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Pump limit"); + return true; + } + } + // report change bigger than 30% + if (activeTemp != null) { + double percentToBeSmallChange = 30; + percentToBeSmallChange /= 100; + double change = percent / (double) activeTemp.tempBasalConvertedToPercent(now, profile); + double lowThreshold = 1 - percentToBeSmallChange; + double highThreshold = 1 + percentToBeSmallChange; + + if (change < lowThreshold || change > highThreshold) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Outside allowed range " + (change * 100) + "%"); + return true; + } + } + if (L.isEnabled(L.APS)) + log.debug("FALSE"); + return false; + } else { + if (activeTemp == null && rate == pump.getBaseBasalRate()) { + if (L.isEnabled(L.APS)) + log.debug("FALSE: No temp running, asking cancel temp"); + return false; + } + if (activeTemp != null && Math.abs(rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) { + if (L.isEnabled(L.APS)) + log.debug("FALSE: Temp equal"); + return false; + } + // always report zerotemp + if (rate == 0) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Zero temp"); + return true; + } + // always report hightemp + if (pump != null && pump.getPumpDescription().tempBasalStyle == PumpDescription.ABSOLUTE) { + double pumpLimit = pump.getPumpDescription().pumpType.getTbrSettings().getMaxDose(); + if (rate == pumpLimit) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Pump limit"); + return true; + } + } + // report change bigger than 30% + if (activeTemp != null) { + double percentToBeSmallChange = 30; + percentToBeSmallChange /= 100; + double change = rate / activeTemp.tempBasalConvertedToAbsolute(now, profile); + double lowThreshold = 1 - percentToBeSmallChange; + double highThreshold = 1 + percentToBeSmallChange; + + if (change < lowThreshold || change > highThreshold) { + if (L.isEnabled(L.APS)) + log.debug("TRUE: Outside allowed range " + (change * 100) + "%"); + return true; + } + } + if (L.isEnabled(L.APS)) + log.debug("FALSE"); + return false; + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java index c9fdb45f86..832e095e81 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/LoopPlugin.java @@ -40,6 +40,7 @@ import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PluginDescription; import info.nightscout.androidaps.interfaces.PluginType; +import info.nightscout.androidaps.interfaces.PumpDescription; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.logging.L; @@ -51,6 +52,7 @@ import info.nightscout.androidaps.plugins.Loop.events.EventLoopSetLastRunGui; import info.nightscout.androidaps.plugins.Loop.events.EventLoopUpdateGui; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; import info.nightscout.androidaps.plugins.NSClientInternal.NSUpload; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.plugins.Wear.ActionStringHandler; import info.nightscout.androidaps.events.EventAcceptOpenLoopChange; @@ -58,6 +60,7 @@ import info.nightscout.androidaps.queue.Callback; import info.nightscout.androidaps.queue.commands.Command; import info.nightscout.utils.FabricPrivacy; import info.nightscout.utils.SP; +import info.nightscout.utils.T; import info.nightscout.utils.ToastUtils; /** @@ -148,8 +151,6 @@ public class LoopPlugin extends PluginBase { * sources and currently only a new BG should trigger a loop run. Hence we return early if * the event causing the calculation is not EventNewBg. *

- * Callers of {@link info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin#runCalculation(String, long, boolean, Event)} - * are sources triggering a calculation which triggers this method upon completion. */ @Subscribe public void onStatusEvent(final EventAutosensCalculationFinished ev) { @@ -305,16 +306,26 @@ public class LoopPlugin extends PluginBase { return; } + // Prepare for pumps using % basals + if (pump.getPumpDescription().tempBasalStyle == PumpDescription.PERCENT) { + result.usePercent = true; + } + result.percent = (int) (result.rate / profile.getBasal() * 100); + // check rate for constrais final APSResult resultAfterConstraints = result.clone(); resultAfterConstraints.rateConstraint = new Constraint<>(resultAfterConstraints.rate); resultAfterConstraints.rate = MainApp.getConstraintChecker().applyBasalConstraints(resultAfterConstraints.rateConstraint, profile).value(); + + resultAfterConstraints.percentConstraint = new Constraint<>(resultAfterConstraints.percent); + resultAfterConstraints.percent = MainApp.getConstraintChecker().applyBasalPercentConstraints(resultAfterConstraints.percentConstraint, profile).value(); + resultAfterConstraints.smbConstraint = new Constraint<>(resultAfterConstraints.smb); resultAfterConstraints.smb = MainApp.getConstraintChecker().applyBolusConstraints(resultAfterConstraints.smbConstraint).value(); // safety check for multiple SMBs long lastBolusTime = TreatmentsPlugin.getPlugin().getLastBolusTime(); - if (lastBolusTime != 0 && lastBolusTime + 3 * 60 * 1000 > System.currentTimeMillis()) { + if (lastBolusTime != 0 && lastBolusTime + T.mins(3).msecs() > System.currentTimeMillis()) { if (L.isEnabled(L.APS)) log.debug("SMB requsted but still in 3 min interval"); resultAfterConstraints.smb = 0; @@ -347,7 +358,7 @@ public class LoopPlugin extends PluginBase { Constraint closedLoopEnabled = MainApp.getConstraintChecker().isClosedLoopAllowed(); if (closedLoopEnabled.value()) { - if (result.isChangeRequested() + if (resultAfterConstraints.isChangeRequested() && !ConfigBuilderPlugin.getCommandQueue().bolusInQueue() && !ConfigBuilderPlugin.getCommandQueue().isRunning(Command.CommandType.BOLUS)) { final PumpEnactResult waiting = new PumpEnactResult(); @@ -390,7 +401,7 @@ public class LoopPlugin extends PluginBase { lastRun.smbSetByPump = null; } } else { - if (result.isChangeRequested() && allowNotification) { + if (resultAfterConstraints.isChangeRequested() && allowNotification) { NotificationCompat.Builder builder = new NotificationCompat.Builder(MainApp.instance().getApplicationContext(), CHANNEL_ID); builder.setSmallIcon(R.drawable.notif_icon) @@ -466,8 +477,12 @@ public class LoopPlugin extends PluginBase { /** * expect absolute request and allow both absolute and percent response based on pump capabilities + * TODO: update pump drivers to support APS request in % */ + public void applyTBRRequest(APSResult request, Profile profile, Callback callback) { + boolean allowPercentage = VirtualPumpPlugin.getPlugin().isEnabled(PluginType.PUMP); + if (!request.tempBasalRequested) { if (callback != null) { callback.result(new PumpEnactResult().enacted(false).success(true).comment(MainApp.gs(R.string.nochangerequested))).run(); @@ -478,9 +493,6 @@ public class LoopPlugin extends PluginBase { PumpInterface pump = MainApp.getConfigBuilder().getActivePump(); TreatmentsInterface activeTreatments = TreatmentsPlugin.getPlugin(); - request.rateConstraint = new Constraint<>(request.rate); - request.rate = MainApp.getConstraintChecker().applyBasalConstraints(request.rateConstraint, profile).value(); - if (!pump.isInitialized()) { if (L.isEnabled(L.APS)) log.debug("applyAPSRequest: " + MainApp.gs(R.string.pumpNotInitialized)); @@ -504,34 +516,66 @@ public class LoopPlugin extends PluginBase { long now = System.currentTimeMillis(); TemporaryBasal activeTemp = activeTreatments.getTempBasalFromHistory(now); - if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) { - if (activeTemp != null) { + if (request.usePercent && allowPercentage) { + if (request.percent == 100 && request.duration == 0) { + if (activeTemp != null) { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: cancelTempBasal()"); + MainApp.getConfigBuilder().getCommandQueue().cancelTempBasal(false, callback); + } else { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: Basal set correctly"); + if (callback != null) { + callback.result(new PumpEnactResult().percent(request.percent).duration(0) + .enacted(false).success(true).comment(MainApp.gs(R.string.basal_set_correctly))).run(); + } + } + } else if (activeTemp != null + && activeTemp.getPlannedRemainingMinutes() > 5 + && request.duration - activeTemp.getPlannedRemainingMinutes() < 30 + && request.percent == activeTemp.percentRate) { if (L.isEnabled(L.APS)) - log.debug("applyAPSRequest: cancelTempBasal()"); - MainApp.getConfigBuilder().getCommandQueue().cancelTempBasal(false, callback); + log.debug("applyAPSRequest: Temp basal set correctly"); + if (callback != null) { + callback.result(new PumpEnactResult().percent(request.percent) + .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes()) + .comment(MainApp.gs(R.string.let_temp_basal_run))).run(); + } } else { if (L.isEnabled(L.APS)) - log.debug("applyAPSRequest: Basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult().absolute(request.rate).duration(0) - .enacted(false).success(true).comment(MainApp.gs(R.string.basal_set_correctly))).run(); - } - } - } else if (activeTemp != null - && activeTemp.getPlannedRemainingMinutes() > 5 - && request.duration - activeTemp.getPlannedRemainingMinutes() < 30 - && Math.abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) { - if (L.isEnabled(L.APS)) - log.debug("applyAPSRequest: Temp basal set correctly"); - if (callback != null) { - callback.result(new PumpEnactResult().absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile)) - .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes()) - .comment(MainApp.gs(R.string.let_temp_basal_run))).run(); + log.debug("applyAPSRequest: tempBasalPercent()"); + MainApp.getConfigBuilder().getCommandQueue().tempBasalPercent(request.percent, request.duration, false, profile, callback); } } else { - if (L.isEnabled(L.APS)) - log.debug("applyAPSRequest: setTempBasalAbsolute()"); - MainApp.getConfigBuilder().getCommandQueue().tempBasalAbsolute(request.rate, request.duration, false, profile, callback); + if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) { + if (activeTemp != null) { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: cancelTempBasal()"); + MainApp.getConfigBuilder().getCommandQueue().cancelTempBasal(false, callback); + } else { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: Basal set correctly"); + if (callback != null) { + callback.result(new PumpEnactResult().absolute(request.rate).duration(0) + .enacted(false).success(true).comment(MainApp.gs(R.string.basal_set_correctly))).run(); + } + } + } else if (activeTemp != null + && activeTemp.getPlannedRemainingMinutes() > 5 + && request.duration - activeTemp.getPlannedRemainingMinutes() < 30 + && Math.abs(request.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < pump.getPumpDescription().basalStep) { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: Temp basal set correctly"); + if (callback != null) { + callback.result(new PumpEnactResult().absolute(activeTemp.tempBasalConvertedToAbsolute(now, profile)) + .enacted(false).success(true).duration(activeTemp.getPlannedRemainingMinutes()) + .comment(MainApp.gs(R.string.let_temp_basal_run))).run(); + } + } else { + if (L.isEnabled(L.APS)) + log.debug("applyAPSRequest: setTempBasalAbsolute()"); + MainApp.getConfigBuilder().getCommandQueue().tempBasalAbsolute(request.rate, request.duration, false, profile, callback); + } } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java index 7d57f52efb..77c8d8a860 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -55,21 +55,10 @@ public class DetermineBasalResultAMA extends APSResult { @Override public DetermineBasalResultAMA clone() { DetermineBasalResultAMA newResult = new DetermineBasalResultAMA(); - newResult.reason = reason; - newResult.rate = rate; - newResult.duration = duration; - newResult.tempBasalRequested = tempBasalRequested; - newResult.rate = rate; - newResult.duration = duration; + doClone(newResult); - try { - newResult.json = new JSONObject(json.toString()); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } newResult.eventualBG = eventualBG; newResult.snoozeBG = snoozeBG; - newResult.date = date; return newResult; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java index d9c577dd42..413b75cd1a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java @@ -203,17 +203,6 @@ public class OpenAPSAMAPlugin extends PluginBase implements APSInterface { // Fix bug determine basal if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) determineBasalResultAMA.tempBasalRequested = false; - // limit requests on openloop mode - if (!MainApp.getConstraintChecker().isClosedLoopAllowed().value()) { - long now = System.currentTimeMillis(); - TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); - if (activeTemp != null && determineBasalResultAMA.rate == 0 && determineBasalResultAMA.duration == 0) { - // going to cancel - } else if (activeTemp != null && Math.abs(determineBasalResultAMA.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < 0.1) { - determineBasalResultAMA.tempBasalRequested = false; - } else if (activeTemp == null && Math.abs(determineBasalResultAMA.rate - ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) < 0.1) - determineBasalResultAMA.tempBasalRequested = false; - } determineBasalResultAMA.iob = iobArray[0]; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java index 3f0e9abea9..d3e1869f87 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java @@ -12,7 +12,6 @@ import info.nightscout.androidaps.plugins.Loop.APSResult; public class DetermineBasalResultMA extends APSResult { private static Logger log = LoggerFactory.getLogger(L.APS); - public JSONObject json = new JSONObject(); private double eventualBG; private double snoozeBG; private String mealAssist; @@ -56,19 +55,8 @@ public class DetermineBasalResultMA extends APSResult { @Override public DetermineBasalResultMA clone() { DetermineBasalResultMA newResult = new DetermineBasalResultMA(); - newResult.reason = new String(reason); - newResult.rate = rate; - newResult.duration = duration; - newResult.tempBasalRequested = isChangeRequested(); - newResult.rate = rate; - newResult.duration = duration; - newResult.tempBasalRequested = isChangeRequested(); + doClone(newResult); - try { - newResult.json = new JSONObject(json.toString()); - } catch (JSONException e) { - log.error("Unhandled exception", e); - } newResult.eventualBG = eventualBG; newResult.snoozeBG = snoozeBG; newResult.mealAssist = mealAssist; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java index b310c3e2b5..954204e47a 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAPlugin.java @@ -182,16 +182,6 @@ public class OpenAPSMAPlugin extends PluginBase implements APSInterface { // Fix bug determinef basal if (determineBasalResultMA.rate == 0d && determineBasalResultMA.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) determineBasalResultMA.tempBasalRequested = false; - // limit requests on openloop mode - if (!MainApp.getConstraintChecker().isClosedLoopAllowed().value()) { - TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); - if (activeTemp != null && determineBasalResultMA.rate == 0 && determineBasalResultMA.duration == 0) { - // going to cancel - } else if (activeTemp != null && Math.abs(determineBasalResultMA.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < 0.1) { - determineBasalResultMA.tempBasalRequested = false; - } else if (activeTemp == null && Math.abs(determineBasalResultMA.rate - ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) < 0.1) - determineBasalResultMA.tempBasalRequested = false; - } determineBasalResultMA.iob = iobTotal; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java index bd41120e30..364731bea5 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/DetermineBasalResultSMB.java @@ -14,8 +14,6 @@ public class DetermineBasalResultSMB extends APSResult { private double eventualBG; private double snoozeBG; - //public double insulinReq; - //public double carbsReq; DetermineBasalResultSMB(JSONObject result) { this(); @@ -70,24 +68,10 @@ public class DetermineBasalResultSMB extends APSResult { @Override public DetermineBasalResultSMB clone() { DetermineBasalResultSMB newResult = new DetermineBasalResultSMB(); - newResult.reason = reason; - newResult.rate = rate; - newResult.duration = duration; - newResult.tempBasalRequested = tempBasalRequested; - newResult.bolusRequested = bolusRequested; - newResult.rate = rate; - newResult.duration = duration; - newResult.smb = smb; - newResult.deliverAt = deliverAt; + doClone(newResult); - try { - newResult.json = new JSONObject(json.toString()); - } catch (JSONException e) { - log.error("Error clone parsing determine-basal result", e); - } newResult.eventualBG = eventualBG; newResult.snoozeBG = snoozeBG; - newResult.date = date; return newResult; } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java index 4445c46a04..9390b9b588 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSSMB/OpenAPSSMBPlugin.java @@ -225,16 +225,6 @@ public class OpenAPSSMBPlugin extends PluginBase implements APSInterface { // Fix bug determine basal if (determineBasalResultSMB.rate == 0d && determineBasalResultSMB.duration == 0 && !TreatmentsPlugin.getPlugin().isTempBasalInProgress()) determineBasalResultSMB.tempBasalRequested = false; - // limit requests on openloop mode - if (!MainApp.getConstraintChecker().isClosedLoopAllowed().value()) { - TemporaryBasal activeTemp = TreatmentsPlugin.getPlugin().getTempBasalFromHistory(now); - if (activeTemp != null && determineBasalResultSMB.rate == 0 && determineBasalResultSMB.duration == 0) { - // going to cancel - } else if (activeTemp != null && Math.abs(determineBasalResultSMB.rate - activeTemp.tempBasalConvertedToAbsolute(now, profile)) < 0.1) { - determineBasalResultSMB.tempBasalRequested = false; - } else if (activeTemp == null && Math.abs(determineBasalResultSMB.rate - ConfigBuilderPlugin.getActivePump().getBaseBasalRate()) < 0.1) - determineBasalResultSMB.tempBasalRequested = false; - } determineBasalResultSMB.iob = iobArray[0]; diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index e52d61169b..d2e7c02e17 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -2,7 +2,6 @@ package info; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import com.squareup.otto.Bus; @@ -10,7 +9,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; import org.powermock.api.mockito.PowerMockito; -import org.powermock.modules.junit4.PowerMockRunner; import java.util.Locale; @@ -21,11 +19,10 @@ import info.nightscout.androidaps.data.ConstraintChecker; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.DatabaseHelper; -import info.nightscout.androidaps.interfaces.Constraint; import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.NSClientInternal.NSUpload; -import info.nightscout.androidaps.plugins.NSClientInternal.data.DbLogger; import info.nightscout.androidaps.plugins.Treatments.TreatmentService; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.androidaps.queue.CommandQueue; @@ -33,10 +30,8 @@ import info.nightscout.utils.SP; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -117,9 +112,10 @@ public class AAPSMocker { when(MainApp.getConfigBuilder()).thenReturn(configBuilderPlugin); } - public static void mockConstraintsChecker() { + public static ConstraintChecker mockConstraintsChecker() { ConstraintChecker constraintChecker = mock(ConstraintChecker.class); when(MainApp.getConstraintChecker()).thenReturn(constraintChecker); + return constraintChecker; } public static void mockBus() { @@ -139,7 +135,7 @@ public class AAPSMocker { when(L.isEnabled(any())).thenReturn(true); } - public static void mockNSUpload(){ + public static void mockNSUpload() { PowerMockito.mockStatic(NSUpload.class); } @@ -159,11 +155,13 @@ public class AAPSMocker { when(ConfigBuilderPlugin.getCommandQueue()).thenReturn(queue); } - public static void mockTreatmentService() throws Exception { + public static TreatmentsPlugin mockTreatmentPlugin() throws Exception { TreatmentService treatmentService = PowerMockito.mock(TreatmentService.class); + PowerMockito.mockStatic(TreatmentsPlugin.class); TreatmentsPlugin treatmentsPlugin = PowerMockito.mock(TreatmentsPlugin.class); PowerMockito.whenNew(TreatmentService.class).withNoArguments().thenReturn(treatmentService); when(TreatmentsPlugin.getPlugin()).thenReturn(treatmentsPlugin); + return treatmentsPlugin; } public static Profile getValidProfile() { @@ -193,6 +191,14 @@ public class AAPSMocker { return profileStore; } + public static void mockProfileFunctions() { + PowerMockito.mockStatic(ProfileFunctions.class); + ProfileFunctions profileFunctions = PowerMockito.mock(ProfileFunctions.class); + PowerMockito.when(ProfileFunctions.getInstance()).thenReturn(profileFunctions); + profile = getValidProfile(); + PowerMockito.when(ProfileFunctions.getInstance().getProfile()).thenReturn(profile); + } + private static MockedBus bus = new MockedBus(); public static void prepareMockedBus() { diff --git a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java index 8fc02d07d2..205ceed465 100644 --- a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java +++ b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.java @@ -156,7 +156,7 @@ public class ConstraintsCheckerTest { // Apply all limits Constraint d = constraintChecker.getMaxBasalAllowed(AAPSMocker.getValidProfile()); Assert.assertEquals(0.8d, d.value()); - Assert.assertEquals(7, d.getReasonList().size()); + Assert.assertEquals(6, d.getReasonList().size()); Assert.assertEquals("DanaR: Limiting basal rate to 0.80 U/h because of pump limit", d.getMostLimitedReasons()); } @@ -183,7 +183,7 @@ public class ConstraintsCheckerTest { // Apply all limits Constraint i = constraintChecker.getMaxBasalPercentAllowed(AAPSMocker.getValidProfile()); Assert.assertEquals((Integer) 100, i.value()); - Assert.assertEquals(10, i.getReasonList().size()); // 7x Safety & RS & R & Insight + Assert.assertEquals(9, i.getReasonList().size()); // 6x Safety & RS & R & Insight Assert.assertEquals("Safety: Limiting percent rate to 100% because of pump limit", i.getMostLimitedReasons()); } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java index 23be73838a..8a11b51667 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java @@ -67,7 +67,7 @@ public class NewNSTreatmentDialogTest { AAPSMocker.mockApplicationContext(); AAPSMocker.mockStrings(); PowerMockito.mockStatic(NSUpload.class); - AAPSMocker.mockTreatmentService(); + AAPSMocker.mockTreatmentPlugin(); AAPSMocker.mockBus(); AAPSMocker.mockDatabaseHelper(); diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPluginTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPluginTest.java index 086b3082c1..ae350c4fe6 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPluginTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/ConstraintsSafety/SafetyPluginTest.java @@ -112,8 +112,7 @@ public class SafetyPluginTest { Assert.assertEquals("Safety: Limiting basal rate to 1.00 U/h because of max value in preferences\n" + "Safety: Limiting basal rate to 4.00 U/h because of max basal multiplier\n" + "Safety: Limiting basal rate to 3.00 U/h because of max daily basal multiplier\n" + - "Safety: Limiting basal rate to 2.00 U/h because of hard limit\n" + - "Safety: Limiting basal rate to 500.00 U/h because of pump limit", c.getReasons()); + "Safety: Limiting basal rate to 2.00 U/h because of hard limit", c.getReasons()); Assert.assertEquals("Safety: Limiting basal rate to 1.00 U/h because of max value in preferences", c.getMostLimitedReasons()); } @@ -145,7 +144,6 @@ public class SafetyPluginTest { "Safety: Limiting basal rate to 4.00 U/h because of max basal multiplier\n" + "Safety: Limiting basal rate to 3.00 U/h because of max daily basal multiplier\n" + "Safety: Limiting basal rate to 2.00 U/h because of hard limit\n" + - "Safety: Limiting basal rate to 500.00 U/h because of pump limit\n" + "Safety: Limiting percent rate to 100% because of pump limit", i.getReasons()); Assert.assertEquals("Safety: Limiting percent rate to 100% because of pump limit", i.getMostLimitedReasons()); } diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java new file mode 100644 index 0000000000..d134b71e79 --- /dev/null +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java @@ -0,0 +1,168 @@ +package info.nightscout.androidaps.plugins.Loop; + +import android.content.Context; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import info.AAPSMocker; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.data.ConstraintChecker; +import info.nightscout.androidaps.db.TemporaryBasal; +import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions; +import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType; +import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.SP; + +import static org.mockito.ArgumentMatchers.anyLong; +import static org.powermock.api.mockito.PowerMockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, SP.class, Context.class, ProfileFunctions.class, TreatmentsPlugin.class}) +public class APSREsultTest { + VirtualPumpPlugin virtualPumpPlugin; + TreatmentsPlugin treatmentsPlugin; + Constraint closedLoopEnabled = new Constraint<>(false); + + @Test + public void isChangeRequestedTest() { + APSResult apsResult = new APSResult(); + + // BASAL RATE IN TEST PROFILE IS 1U/h + + // **** PERCENT pump **** + virtualPumpPlugin.getPumpDescription().setPumpDescription(PumpType.Cellnovo1); // % based + apsResult.usePercent(true); + + // closed loop mode return original request + closedLoopEnabled.set(true); + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(null); + apsResult.tempBasalRequested(false); + Assert.assertEquals(false, apsResult.isChangeRequested()); + apsResult.tempBasalRequested(true).percent(200).duration(30); + Assert.assertEquals(true, apsResult.isChangeRequested()); + + // open loop + closedLoopEnabled.set(false); + // no change requested + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(null); + apsResult.tempBasalRequested(false); + Assert.assertEquals(false, apsResult.isChangeRequested()); + + // request 100% when no temp is running + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(null); + apsResult.tempBasalRequested(true).percent(100).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request equal temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(70).duration(30)); + apsResult.tempBasalRequested(true).percent(70).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request zero temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(10).duration(30)); + apsResult.tempBasalRequested(true).percent(0).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // request high temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(190).duration(30)); + apsResult.tempBasalRequested(true).percent(200).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // request slightly different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(70).duration(30)); + apsResult.tempBasalRequested(true).percent(80).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(70).duration(30)); + apsResult.tempBasalRequested(true).percent(120).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // it should work with absolute temps too + // request different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(1).duration(30)); + apsResult.tempBasalRequested(true).percent(100).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(2).duration(30)); + apsResult.tempBasalRequested(true).percent(50).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // **** ABSOLUTE pump **** + virtualPumpPlugin.getPumpDescription().setPumpDescription(PumpType.Medtronic_515_715); // U/h based + apsResult.usePercent(false); + + // open loop + closedLoopEnabled.set(false); + // request 100% when no temp is running + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(null); + apsResult.tempBasalRequested(true).rate(1).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request equal temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(2).duration(30)); + apsResult.tempBasalRequested(true).rate(2).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(200).duration(30)); + apsResult.tempBasalRequested(true).rate(2).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request zero temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(0.1d).duration(30)); + apsResult.tempBasalRequested(true).rate(0).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // request high temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(34.9).duration(30)); + apsResult.tempBasalRequested(true).rate(35).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // request slightly different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(1.1d).duration(30)); + apsResult.tempBasalRequested(true).rate(1.2d).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + // request different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().absolute(1.1d).duration(30)); + apsResult.tempBasalRequested(true).rate(1.5d).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + // it should work with percent temps too + // request different temp + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(110).duration(30)); + apsResult.tempBasalRequested(true).rate(1.1d).duration(30); + Assert.assertEquals(false , apsResult.isChangeRequested()); + + when(treatmentsPlugin.getTempBasalFromHistory(anyLong())).thenReturn(new TemporaryBasal().percent(200).duration(30)); + apsResult.tempBasalRequested(true).rate(0.5d).duration(30); + Assert.assertEquals(true , apsResult.isChangeRequested()); + + } + + + @Before + public void prepareMock() throws Exception { + AAPSMocker.mockMainApp(); + AAPSMocker.mockConfigBuilder(); + AAPSMocker.mockSP(); + AAPSMocker.mockStrings(); + AAPSMocker.mockBus(); + AAPSMocker.mockProfileFunctions(); + treatmentsPlugin = AAPSMocker.mockTreatmentPlugin(); + ConstraintChecker constraintChecker = AAPSMocker.mockConstraintsChecker(); + + virtualPumpPlugin = VirtualPumpPlugin.getPlugin(); + when(ConfigBuilderPlugin.getActivePump()).thenReturn(virtualPumpPlugin); + + when(constraintChecker.isClosedLoopAllowed()).thenReturn(closedLoopEnabled); + } +} From 6f5332af0ac1d9b073dcf39f73fcfb2b8d1b2fb2 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 4 Sep 2018 20:42:33 +0200 Subject: [PATCH 2/6] Omnipod max tbr 30U --- .../nightscout/androidaps/plugins/PumpCommon/defs/PumpType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCommon/defs/PumpType.java b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCommon/defs/PumpType.java index be759548e0..1ab285fb27 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/PumpCommon/defs/PumpType.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/PumpCommon/defs/PumpType.java @@ -87,7 +87,7 @@ public enum PumpType { Insulet_Omnipod("Insulet Omnipod", 0.05d, null, // new DoseSettings(0.05d, 30, 8*60, 0.05d), // PumpTempBasalType.Absolute, // - new DoseSettings(0.05d, 30, 12*60, 0d, 5.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr + new DoseSettings(0.05d, 30, 12*60, 0d, 30.0d), PumpCapability.BasalRate_Duration30minAllowed, // cannot exceed max basal rate 30u/hr 0.05d, 0.05d, null, PumpCapability.VirtualPumpCapabilities), // Medtronic From 1ede3df6270886887a1c17152cad36443c790e42 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 4 Sep 2018 20:50:42 +0200 Subject: [PATCH 3/6] fix tests --- app/src/test/java/info/AAPSMocker.java | 9 ++++++--- .../Careportal/Dialogs/NewNSTreatmentDialogTest.java | 2 +- .../androidaps/plugins/Loop/APSREsultTest.java | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/test/java/info/AAPSMocker.java b/app/src/test/java/info/AAPSMocker.java index d2e7c02e17..15bab41e2b 100644 --- a/app/src/test/java/info/AAPSMocker.java +++ b/app/src/test/java/info/AAPSMocker.java @@ -155,15 +155,18 @@ public class AAPSMocker { when(ConfigBuilderPlugin.getCommandQueue()).thenReturn(queue); } - public static TreatmentsPlugin mockTreatmentPlugin() throws Exception { - TreatmentService treatmentService = PowerMockito.mock(TreatmentService.class); + public static TreatmentsPlugin mockTreatmentPlugin() { PowerMockito.mockStatic(TreatmentsPlugin.class); TreatmentsPlugin treatmentsPlugin = PowerMockito.mock(TreatmentsPlugin.class); - PowerMockito.whenNew(TreatmentService.class).withNoArguments().thenReturn(treatmentService); when(TreatmentsPlugin.getPlugin()).thenReturn(treatmentsPlugin); return treatmentsPlugin; } + public static void mockTreatmentService() throws Exception { + TreatmentService treatmentService = PowerMockito.mock(TreatmentService.class); + PowerMockito.whenNew(TreatmentService.class).withNoArguments().thenReturn(treatmentService); + } + public static Profile getValidProfile() { try { if (profile == null) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java index 8a11b51667..23be73838a 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Careportal/Dialogs/NewNSTreatmentDialogTest.java @@ -67,7 +67,7 @@ public class NewNSTreatmentDialogTest { AAPSMocker.mockApplicationContext(); AAPSMocker.mockStrings(); PowerMockito.mockStatic(NSUpload.class); - AAPSMocker.mockTreatmentPlugin(); + AAPSMocker.mockTreatmentService(); AAPSMocker.mockBus(); AAPSMocker.mockDatabaseHelper(); diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java index d134b71e79..83a8e6a69a 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java @@ -157,6 +157,7 @@ public class APSREsultTest { AAPSMocker.mockStrings(); AAPSMocker.mockBus(); AAPSMocker.mockProfileFunctions(); + AAPSMocker.mockTreatmentService(); treatmentsPlugin = AAPSMocker.mockTreatmentPlugin(); ConstraintChecker constraintChecker = AAPSMocker.mockConstraintsChecker(); From f3a4f883a41f87813b8a342bfa180b7bb114b901 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 4 Sep 2018 21:51:13 +0200 Subject: [PATCH 4/6] fix tests --- .../info/nightscout/androidaps/plugins/Loop/APSREsultTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java index 83a8e6a69a..a47010d06d 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java @@ -158,6 +158,7 @@ public class APSREsultTest { AAPSMocker.mockBus(); AAPSMocker.mockProfileFunctions(); AAPSMocker.mockTreatmentService(); + AAPSMocker.mockL(); treatmentsPlugin = AAPSMocker.mockTreatmentPlugin(); ConstraintChecker constraintChecker = AAPSMocker.mockConstraintsChecker(); From e36499f9d45040d4447c1dafb6677411d984a217 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 4 Sep 2018 22:08:35 +0200 Subject: [PATCH 5/6] more tests --- .../androidaps/plugins/Loop/APSResult.java | 8 ++++--- .../plugins/Loop/APSREsultTest.java | 23 ++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java index 374bfbcd1d..cd7c0b9a0e 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Loop/APSResult.java @@ -68,18 +68,20 @@ public class APSResult { return this; } - public APSResult tempBasalRequested(boolean tempBasalRequested) { this.tempBasalRequested = tempBasalRequested; return this; } - public APSResult usePercent(boolean usePercent) { this.usePercent = usePercent; return this; } + public APSResult json(JSONObject json) { + this.json = json; + return this; + } @Override public String toString() { @@ -151,7 +153,7 @@ public class APSResult { protected void doClone(APSResult newResult) { newResult.date = date; - newResult.reason = new String(reason); + newResult.reason = reason != null ? new String(reason) : null; newResult.rate = rate; newResult.duration = duration; newResult.tempBasalRequested = tempBasalRequested; diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java index a47010d06d..5dcefc2f8f 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java @@ -14,18 +14,20 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.ConstraintChecker; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.interfaces.Constraint; +import info.nightscout.androidaps.logging.L; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConfigBuilder.ProfileFunctions; import info.nightscout.androidaps.plugins.PumpCommon.defs.PumpType; import info.nightscout.androidaps.plugins.PumpVirtual.VirtualPumpPlugin; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.JsonHelper; import info.nightscout.utils.SP; import static org.mockito.ArgumentMatchers.anyLong; import static org.powermock.api.mockito.PowerMockito.when; @RunWith(PowerMockRunner.class) -@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, SP.class, Context.class, ProfileFunctions.class, TreatmentsPlugin.class}) +@PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, SP.class, Context.class, ProfileFunctions.class, TreatmentsPlugin.class, L.class}) public class APSREsultTest { VirtualPumpPlugin virtualPumpPlugin; TreatmentsPlugin treatmentsPlugin; @@ -148,6 +150,25 @@ public class APSREsultTest { } + @Test + public void cloneTest() { + APSResult apsResult = new APSResult(); + apsResult.rate(10); + + APSResult apsResult2 = apsResult.clone(); + Assert.assertEquals(apsResult.rate, apsResult2.rate, 0); + } + + @Test + public void jsonTest() { + closedLoopEnabled.set(true); + APSResult apsResult = new APSResult(); + apsResult.rate(20).tempBasalRequested(true); + Assert.assertEquals(20d, JsonHelper.safeGetDouble(apsResult.json(), "rate"), 0d); + + apsResult.rate(20).tempBasalRequested(false); + Assert.assertEquals(false, apsResult.json().has("rate")); + } @Before public void prepareMock() throws Exception { From abea2f30bbfcbd9fe7cab210276f2b6a1bda614c Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 6 Sep 2018 07:45:52 +0200 Subject: [PATCH 6/6] typo --- .../plugins/Loop/{APSREsultTest.java => APSResultTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename app/src/test/java/info/nightscout/androidaps/plugins/Loop/{APSREsultTest.java => APSResultTest.java} (99%) diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSResultTest.java similarity index 99% rename from app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java rename to app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSResultTest.java index 5dcefc2f8f..1505004535 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSREsultTest.java +++ b/app/src/test/java/info/nightscout/androidaps/plugins/Loop/APSResultTest.java @@ -28,7 +28,7 @@ import static org.powermock.api.mockito.PowerMockito.when; @RunWith(PowerMockRunner.class) @PrepareForTest({MainApp.class, ConfigBuilderPlugin.class, SP.class, Context.class, ProfileFunctions.class, TreatmentsPlugin.class, L.class}) -public class APSREsultTest { +public class APSResultTest { VirtualPumpPlugin virtualPumpPlugin; TreatmentsPlugin treatmentsPlugin; Constraint closedLoopEnabled = new Constraint<>(false);