Merge pull request #1392 from MilosKozak/openloop

Openloop
This commit is contained in:
Milos Kozak 2018-09-07 12:55:25 +02:00 committed by GitHub
commit 55d1dd0847
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 502 additions and 150 deletions

View file

@ -301,6 +301,16 @@ public class TemporaryBasal implements Interval {
}
}
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;
}
}
public String toString() {
return "TemporaryBasal{" +
"date=" + date +

View file

@ -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;
}

View file

@ -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,39 @@ public class APSResult {
public Constraint<Double> inputConstraints;
public Constraint<Double> rateConstraint;
public Constraint<Integer> percentConstraint;
public Constraint<Double> 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;
}
public APSResult json(JSONObject json) {
this.json = json;
return this;
}
@Override
public String toString() {
final PumpInterface pump = ConfigBuilderPlugin.getActivePump();
@ -55,6 +93,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 +122,10 @@ public class APSResult {
ret = MainApp.gs(R.string.canceltemp) + "<br>";
else if (rate == -1)
ret = MainApp.gs(R.string.let_temp_basal_run) + "<br>";
else if (usePercent)
ret = "<b>" + MainApp.gs(R.string.rate) + "</b>: " + DecimalFormatter.to2Decimal(percent) + "% " +
"(" + DecimalFormatter.to2Decimal(percent * pump.getBaseBasalRate() / 100d) + " U/h)<br>" +
"<b>" + MainApp.gs(R.string.duration) + "</b>: " + DecimalFormatter.to2Decimal(duration) + " min<br>";
else
ret = "<b>" + MainApp.gs(R.string.rate) + "</b>: " + DecimalFormatter.to2Decimal(rate) + " U/h " +
"(" + DecimalFormatter.to2Decimal(rate / pump.getBaseBasalRate() * 100) + "%) <br>" +
@ -101,21 +147,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 = reason != null ? new String(reason) : null;
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 +286,112 @@ public class APSResult {
}
public boolean isChangeRequested() {
Constraint<Boolean> 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;
}
}
}

View file

@ -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.
* <p>
* 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<Boolean> 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,6 +516,37 @@ public class LoopPlugin extends PluginBase {
long now = System.currentTimeMillis();
TemporaryBasal activeTemp = activeTreatments.getTempBasalFromHistory(now);
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: 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: tempBasalPercent()");
MainApp.getConfigBuilder().getCommandQueue().tempBasalPercent(request.percent, request.duration, false, profile, callback);
}
} else {
if ((request.rate == 0 && request.duration == 0) || Math.abs(request.rate - pump.getBaseBasalRate()) < pump.getPumpDescription().basalStep) {
if (activeTemp != null) {
if (L.isEnabled(L.APS))
@ -534,6 +577,7 @@ public class LoopPlugin extends PluginBase {
MainApp.getConfigBuilder().getCommandQueue().tempBasalAbsolute(request.rate, request.duration, false, profile, callback);
}
}
}
public void applySMBRequest(APSResult request, Callback callback) {
if (!request.bolusRequested) {

View file

@ -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;
}

View file

@ -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];

View file

@ -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;

View file

@ -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;

View file

@ -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;
}

View file

@ -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];

View file

@ -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

View file

@ -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() {
@ -159,11 +155,16 @@ public class AAPSMocker {
when(ConfigBuilderPlugin.getCommandQueue()).thenReturn(queue);
}
public static TreatmentsPlugin mockTreatmentPlugin() {
PowerMockito.mockStatic(TreatmentsPlugin.class);
TreatmentsPlugin treatmentsPlugin = PowerMockito.mock(TreatmentsPlugin.class);
when(TreatmentsPlugin.getPlugin()).thenReturn(treatmentsPlugin);
return treatmentsPlugin;
}
public static void mockTreatmentService() throws Exception {
TreatmentService treatmentService = PowerMockito.mock(TreatmentService.class);
TreatmentsPlugin treatmentsPlugin = PowerMockito.mock(TreatmentsPlugin.class);
PowerMockito.whenNew(TreatmentService.class).withNoArguments().thenReturn(treatmentService);
when(TreatmentsPlugin.getPlugin()).thenReturn(treatmentsPlugin);
}
public static Profile getValidProfile() {
@ -193,6 +194,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() {

View file

@ -156,7 +156,7 @@ public class ConstraintsCheckerTest {
// Apply all limits
Constraint<Double> 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<Integer> 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());
}

View file

@ -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());
}

View file

@ -0,0 +1,191 @@
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.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, L.class})
public class APSResultTest {
VirtualPumpPlugin virtualPumpPlugin;
TreatmentsPlugin treatmentsPlugin;
Constraint<Boolean> 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());
}
@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 {
AAPSMocker.mockMainApp();
AAPSMocker.mockConfigBuilder();
AAPSMocker.mockSP();
AAPSMocker.mockStrings();
AAPSMocker.mockBus();
AAPSMocker.mockProfileFunctions();
AAPSMocker.mockTreatmentService();
AAPSMocker.mockL();
treatmentsPlugin = AAPSMocker.mockTreatmentPlugin();
ConstraintChecker constraintChecker = AAPSMocker.mockConstraintsChecker();
virtualPumpPlugin = VirtualPumpPlugin.getPlugin();
when(ConfigBuilderPlugin.getActivePump()).thenReturn(virtualPumpPlugin);
when(constraintChecker.isClosedLoopAllowed()).thenReturn(closedLoopEnabled);
}
}