From faf8e07248b040a0e39e7f1078e3bda372097ff4 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 3 Jan 2017 21:01:01 +0100 Subject: [PATCH 01/51] renaming before adding AMA --- .../info/nightscout/androidaps/Config.java | 2 +- .../info/nightscout/androidaps/MainApp.java | 2 +- .../androidaps/PreferencesActivity.java | 3 +- .../interfaces/TreatmentsInterface.java | 1 - .../CircadianPercentageProfilePlugin.java | 1 - .../ConfigBuilder/ConfigBuilderPlugin.java | 7 +-- ...JS.java => DetermineBasalAdapterMAJS.java} | 25 ++++----- ...esult.java => DetermineBasalResultMA.java} | 22 ++++---- .../plugins/OpenAPSMA/OpenAPSMAFragment.java | 25 ++++----- .../plugins/OpenAPSMA/OpenAPSMAPlugin.java | 56 +++++++++---------- ...ateGui.java => EventOpenAPSUpdateGui.java} | 2 +- ....java => EventOpenAPSUpdateResultGui.java} | 4 +- .../Overview/Dialogs/WizardDialog.java | 3 - .../PersistentNotificationPlugin.java | 1 - .../info/nightscout/utils/BolusWizard.java | 1 - 15 files changed, 69 insertions(+), 86 deletions(-) rename app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/{DetermineBasalAdapterJS.java => DetermineBasalAdapterMAJS.java} (93%) rename app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/{DetermineBasalResult.java => DetermineBasalResultMA.java} (81%) rename app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/{EventOpenAPSMAUpdateGui.java => EventOpenAPSUpdateGui.java} (73%) rename app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/{EventOpenAPSMAUpdateResultGui.java => EventOpenAPSUpdateResultGui.java} (62%) diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index 62bdc6eb1a..de4dc5d49b 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -7,7 +7,7 @@ public class Config { // MAIN FUCTIONALITY public static final boolean APS = BuildConfig.APS; // PLUGINS - public static final boolean OPENAPSMAENABLED = APS; + public static final boolean OPENAPSENABLED = APS; public static final boolean LOOPENABLED = APS; public static final boolean WEAR = BuildConfig.WEAR; diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 2c5f46a259..2d7ebbde2c 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -82,7 +82,7 @@ public class MainApp extends Application { pluginsList.add(MDIFragment.getPlugin()); pluginsList.add(VirtualPumpFragment.getPlugin()); if (Config.LOOPENABLED) pluginsList.add(LoopFragment.getPlugin()); - if (Config.OPENAPSMAENABLED) pluginsList.add(OpenAPSMAFragment.getPlugin()); + if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSMAFragment.getPlugin()); pluginsList.add(NSProfileFragment.getPlugin()); pluginsList.add(SimpleProfileFragment.getPlugin()); pluginsList.add(LocalProfileFragment.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java index f6edcdd827..6f8b439268 100644 --- a/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java +++ b/app/src/main/java/info/nightscout/androidaps/PreferencesActivity.java @@ -15,7 +15,6 @@ import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.events.EventRefreshGui; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.DanaR.BluetoothDevicePreference; -import info.nightscout.androidaps.plugins.DanaR.DanaRFragment; import info.nightscout.androidaps.plugins.DanaR.DanaRPlugin; import info.nightscout.androidaps.plugins.DanaRKorean.DanaRKoreanPlugin; import info.nightscout.utils.LocaleHelper; @@ -85,7 +84,7 @@ public class PreferencesActivity extends PreferenceActivity implements SharedPre addPreferencesFromResource(R.xml.pref_treatments); if (Config.APS) addPreferencesFromResource(R.xml.pref_closedmode); - if (Config.OPENAPSMAENABLED) + if (Config.OPENAPSENABLED) addPreferencesFromResource(R.xml.pref_openapsma); addPreferencesFromResource(R.xml.pref_nightscout); if (Config.DANAR) { diff --git a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java index 26641193b4..0b008d1a4d 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -4,7 +4,6 @@ import java.util.List; import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; -import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; /** diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java index 274e1a941d..396b67fafc 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/CircadianPercentageProfile/CircadianPercentageProfilePlugin.java @@ -15,7 +15,6 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.ProfileInterface; -import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAPlugin; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.SafeParse; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java index ccbec0581f..0a83c86296 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/ConfigBuilder/ConfigBuilderPlugin.java @@ -43,13 +43,12 @@ import info.nightscout.androidaps.plugins.DanaR.comm.MsgError; import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.DeviceStatus; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; -import info.nightscout.androidaps.plugins.OpenAPSMA.DetermineBasalResult; +import info.nightscout.androidaps.plugins.OpenAPSMA.DetermineBasalResultMA; import info.nightscout.androidaps.plugins.Overview.Dialogs.BolusProgressDialog; import info.nightscout.androidaps.plugins.Actions.dialogs.NewExtendedBolusDialog; import info.nightscout.androidaps.plugins.Overview.Notification; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; -import info.nightscout.androidaps.plugins.SmsCommunicator.SmsCommunicatorPlugin; import info.nightscout.client.data.DbLogger; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DateUtil; @@ -901,8 +900,8 @@ public class ConfigBuilderPlugin implements PluginBase, PumpInterface, Constrain apsResult.json().put("timestamp", DateUtil.toISOString(lastRun.lastAPSRun)); deviceStatus.suggested = apsResult.json(); - if (lastRun.request instanceof DetermineBasalResult) { - DetermineBasalResult result = (DetermineBasalResult) lastRun.request; + if (lastRun.request instanceof DetermineBasalResultMA) { + DetermineBasalResultMA result = (DetermineBasalResultMA) lastRun.request; deviceStatus.iob = result.iob.json(); deviceStatus.iob.put("time", DateUtil.toISOString(lastRun.lastAPSRun)); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java similarity index 93% rename from app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterJS.java rename to app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java index ca62b7a197..db3c42bc20 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterJS.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalAdapterMAJS.java @@ -19,12 +19,11 @@ import info.nightscout.androidaps.Config; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.plugins.Loop.ScriptReader; -import info.nightscout.androidaps.plugins.Treatments.TreatmentsFragment; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; import info.nightscout.client.data.NSProfile; -public class DetermineBasalAdapterJS implements Parcelable { - private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterJS.class); +public class DetermineBasalAdapterMAJS implements Parcelable { + private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterMAJS.class); private ScriptReader mScriptReader = null; @@ -51,7 +50,7 @@ public class DetermineBasalAdapterJS implements Parcelable { * Parcelable implementation * result string for display only **/ - protected DetermineBasalAdapterJS(Parcel in) { + protected DetermineBasalAdapterMAJS(Parcel in) { storedCurrentTemp = in.readString(); storedIobData = in.readString(); storedGlucoseStatus = in.readString(); @@ -73,15 +72,15 @@ public class DetermineBasalAdapterJS implements Parcelable { return 0; } - public static final Creator CREATOR = new Creator() { + public static final Creator CREATOR = new Creator() { @Override - public DetermineBasalAdapterJS createFromParcel(Parcel in) { - return new DetermineBasalAdapterJS(in); + public DetermineBasalAdapterMAJS createFromParcel(Parcel in) { + return new DetermineBasalAdapterMAJS(in); } @Override - public DetermineBasalAdapterJS[] newArray(int size) { - return new DetermineBasalAdapterJS[size]; + public DetermineBasalAdapterMAJS[] newArray(int size) { + return new DetermineBasalAdapterMAJS[size]; } }; @@ -89,7 +88,7 @@ public class DetermineBasalAdapterJS implements Parcelable { * Main code */ - public DetermineBasalAdapterJS(ScriptReader scriptReader) throws IOException { + public DetermineBasalAdapterMAJS(ScriptReader scriptReader) throws IOException { mV8rt = V8.createV8Runtime(); mScriptReader = scriptReader; @@ -143,7 +142,7 @@ public class DetermineBasalAdapterJS implements Parcelable { mV8rt.add(PARAM_meal_data, mMealData); } - public DetermineBasalResult invoke() { + public DetermineBasalResultMA invoke() { mV8rt.executeVoidScript( "console.error(\"determine_basal(\"+\n" + "JSON.stringify(" + PARAM_glucoseStatus + ")+ \", \" +\n" + @@ -170,9 +169,9 @@ public class DetermineBasalAdapterJS implements Parcelable { V8Object v8ObjectReuslt = mV8rt.getObject("rT"); - DetermineBasalResult result = null; + DetermineBasalResultMA result = null; try { - result = new DetermineBasalResult(v8ObjectReuslt, new JSONObject(ret)); + result = new DetermineBasalResultMA(v8ObjectReuslt, new JSONObject(ret)); } catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResult.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java similarity index 81% rename from app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResult.java rename to app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java index 9ea8286c7e..04756cb434 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResult.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/DetermineBasalResultMA.java @@ -10,7 +10,7 @@ import org.json.JSONObject; import info.nightscout.androidaps.plugins.Loop.APSResult; -public class DetermineBasalResult extends APSResult { +public class DetermineBasalResultMA extends APSResult { public JSONObject json = new JSONObject(); public double eventualBG; @@ -18,7 +18,7 @@ public class DetermineBasalResult extends APSResult { public String mealAssist; public IobTotal iob; - public DetermineBasalResult(V8Object result, JSONObject j) { + public DetermineBasalResultMA(V8Object result, JSONObject j) { json = j; if (result.contains("error")) { reason = result.getString("error"); @@ -61,17 +61,17 @@ public class DetermineBasalResult extends APSResult { dest.writeString(mealAssist); } - public final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public DetermineBasalResult createFromParcel(Parcel in) { - return new DetermineBasalResult(in); + public final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DetermineBasalResultMA createFromParcel(Parcel in) { + return new DetermineBasalResultMA(in); } - public DetermineBasalResult[] newArray(int size) { - return new DetermineBasalResult[size]; + public DetermineBasalResultMA[] newArray(int size) { + return new DetermineBasalResultMA[size]; } }; - private DetermineBasalResult(Parcel in) { + private DetermineBasalResultMA(Parcel in) { super(in); try { json = new JSONObject(in.readString()); @@ -83,12 +83,12 @@ public class DetermineBasalResult extends APSResult { mealAssist = in.readString(); } - public DetermineBasalResult() { + public DetermineBasalResultMA() { } @Override - public DetermineBasalResult clone() { - DetermineBasalResult newResult = new DetermineBasalResult(); + public DetermineBasalResultMA clone() { + DetermineBasalResultMA newResult = new DetermineBasalResultMA(); newResult.reason = new String(reason); newResult.rate = rate; newResult.duration = duration; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAFragment.java index 86cd729dcb..b547889568 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/OpenAPSMAFragment.java @@ -17,9 +17,8 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.FragmentBase; -import info.nightscout.androidaps.plugins.Loop.APSResult; -import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSMAUpdateGui; -import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSMAUpdateResultGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; import info.nightscout.utils.JSONFormatter; public class OpenAPSMAFragment extends Fragment implements View.OnClickListener, FragmentBase { @@ -87,12 +86,12 @@ public class OpenAPSMAFragment extends Fragment implements View.OnClickListener, } @Subscribe - public void onStatusEvent(final EventOpenAPSMAUpdateGui ev) { + public void onStatusEvent(final EventOpenAPSUpdateGui ev) { updateGUI(); } @Subscribe - public void onStatusEvent(final EventOpenAPSMAUpdateResultGui ev) { + public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { updateResultGUI(ev.text); } @@ -102,18 +101,18 @@ public class OpenAPSMAFragment extends Fragment implements View.OnClickListener, activity.runOnUiThread(new Runnable() { @Override public void run() { - DetermineBasalResult lastAPSResult = getPlugin().lastAPSResult; + DetermineBasalResultMA lastAPSResult = getPlugin().lastAPSResult; if (lastAPSResult != null) { resultView.setText(JSONFormatter.format(lastAPSResult.json)); requestView.setText(lastAPSResult.toSpanned()); } - DetermineBasalAdapterJS determineBasalAdapterJS = getPlugin().lastDetermineBasalAdapterJS; - if (determineBasalAdapterJS != null) { - glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterJS.getGlucoseStatusParam())); - currentTempView.setText(JSONFormatter.format(determineBasalAdapterJS.getCurrentTempParam())); - iobDataView.setText(JSONFormatter.format(determineBasalAdapterJS.getIobDataParam())); - profileView.setText(JSONFormatter.format(determineBasalAdapterJS.getProfileParam())); - mealDataView.setText(JSONFormatter.format(determineBasalAdapterJS.getMealDataParam())); + DetermineBasalAdapterMAJS determineBasalAdapterMAJS = getPlugin().lastDetermineBasalAdapterMAJS; + if (determineBasalAdapterMAJS != null) { + glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getGlucoseStatusParam())); + currentTempView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getCurrentTempParam())); + iobDataView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getIobDataParam())); + profileView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getProfileParam())); + mealDataView.setText(JSONFormatter.format(determineBasalAdapterMAJS.getMealDataParam())); } if (getPlugin().lastAPSRun != null) { lastRunView.setText(getPlugin().lastAPSRun.toLocaleString()); 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 47ef05e08f..359d752f02 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 @@ -1,13 +1,9 @@ package info.nightscout.androidaps.plugins.OpenAPSMA; -import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.os.Bundle; import android.preference.PreferenceManager; import org.json.JSONException; -import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,7 +14,6 @@ import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; -import info.nightscout.androidaps.Services.Intents; import info.nightscout.androidaps.db.DatabaseHelper; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.PluginBase; @@ -27,10 +22,9 @@ import info.nightscout.androidaps.interfaces.TempBasalsInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.ScriptReader; -import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSMAUpdateGui; -import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSMAUpdateResultGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; -import info.nightscout.client.data.DbLogger; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DateUtil; import info.nightscout.utils.Round; @@ -44,9 +38,9 @@ public class OpenAPSMAPlugin implements PluginBase, APSInterface { private static Logger log = LoggerFactory.getLogger(OpenAPSMAPlugin.class); // last values - DetermineBasalAdapterJS lastDetermineBasalAdapterJS = null; + DetermineBasalAdapterMAJS lastDetermineBasalAdapterMAJS = null; Date lastAPSRun = null; - DetermineBasalResult lastAPSResult = null; + DetermineBasalResultMA lastAPSResult = null; boolean fragmentEnabled = false; boolean fragmentVisible = true; @@ -104,9 +98,9 @@ public class OpenAPSMAPlugin implements PluginBase, APSInterface { @Override public void invoke() { lastAPSResult = null; - DetermineBasalAdapterJS determineBasalAdapterJS = null; + DetermineBasalAdapterMAJS determineBasalAdapterMAJS = null; try { - determineBasalAdapterJS = new DetermineBasalAdapterJS(new ScriptReader(MainApp.instance().getBaseContext())); + determineBasalAdapterMAJS = new DetermineBasalAdapterMAJS(new ScriptReader(MainApp.instance().getBaseContext())); } catch (IOException e) { log.error(e.getMessage(), e); return; @@ -117,28 +111,28 @@ public class OpenAPSMAPlugin implements PluginBase, APSInterface { PumpInterface pump = MainApp.getConfigBuilder(); if (!isEnabled(PluginBase.APS)) { - MainApp.bus().post(new EventOpenAPSMAUpdateResultGui(MainApp.instance().getString(R.string.openapsma_disabled))); + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_disabled))); if (Config.logAPSResult) log.debug(MainApp.instance().getString(R.string.openapsma_disabled)); return; } if (glucoseStatus == null) { - MainApp.bus().post(new EventOpenAPSMAUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noglucosedata))); + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noglucosedata))); if (Config.logAPSResult) log.debug(MainApp.instance().getString(R.string.openapsma_noglucosedata)); return; } if (profile == null) { - MainApp.bus().post(new EventOpenAPSMAUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noprofile))); + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noprofile))); if (Config.logAPSResult) log.debug(MainApp.instance().getString(R.string.openapsma_noprofile)); return; } if (pump == null) { - MainApp.bus().post(new EventOpenAPSMAUpdateResultGui(MainApp.instance().getString(R.string.openapsma_nopump))); + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_nopump))); if (Config.logAPSResult) log.debug(MainApp.instance().getString(R.string.openapsma_nopump)); return; @@ -193,37 +187,37 @@ public class OpenAPSMAPlugin implements PluginBase, APSInterface { if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return; if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return; - determineBasalAdapterJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobTotal, glucoseStatus, mealData); + determineBasalAdapterMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobTotal, glucoseStatus, mealData); - DetermineBasalResult determineBasalResult = determineBasalAdapterJS.invoke(); + DetermineBasalResultMA determineBasalResultMA = determineBasalAdapterMAJS.invoke(); // Fix bug determine basal - if (determineBasalResult.rate == 0d && determineBasalResult.duration == 0 && !MainApp.getConfigBuilder().isTempBasalInProgress()) - determineBasalResult.changeRequested = false; + if (determineBasalResultMA.rate == 0d && determineBasalResultMA.duration == 0 && !MainApp.getConfigBuilder().isTempBasalInProgress()) + determineBasalResultMA.changeRequested = false; // limit requests on openloop mode if (!MainApp.getConfigBuilder().isClosedModeEnabled()) { - if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResult.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRate()) < 0.1) - determineBasalResult.changeRequested = false; - if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResult.rate - MainApp.getConfigBuilder().getBaseBasalRate()) < 0.1) - determineBasalResult.changeRequested = false; + if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultMA.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRate()) < 0.1) + determineBasalResultMA.changeRequested = false; + if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultMA.rate - MainApp.getConfigBuilder().getBaseBasalRate()) < 0.1) + determineBasalResultMA.changeRequested = false; } - determineBasalResult.iob = iobTotal; + determineBasalResultMA.iob = iobTotal; - determineBasalAdapterJS.release(); + determineBasalAdapterMAJS.release(); try { - determineBasalResult.json.put("timestamp", DateUtil.toISOString(now)); + determineBasalResultMA.json.put("timestamp", DateUtil.toISOString(now)); } catch (JSONException e) { e.printStackTrace(); } - lastDetermineBasalAdapterJS = determineBasalAdapterJS; - lastAPSResult = determineBasalResult; + lastDetermineBasalAdapterMAJS = determineBasalAdapterMAJS; + lastAPSResult = determineBasalResultMA; lastAPSRun = now; - MainApp.bus().post(new EventOpenAPSMAUpdateGui()); + MainApp.bus().post(new EventOpenAPSUpdateGui()); - //deviceStatus.suggested = determineBasalResult.json; + //deviceStatus.suggested = determineBasalResultMA.json; } // safety checks diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateGui.java similarity index 73% rename from app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateGui.java rename to app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateGui.java index 528473183e..65b3b49d53 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateGui.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateGui.java @@ -3,5 +3,5 @@ package info.nightscout.androidaps.plugins.OpenAPSMA.events; /** * Created by mike on 05.08.2016. */ -public class EventOpenAPSMAUpdateGui { +public class EventOpenAPSUpdateGui { } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateResultGui.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateResultGui.java similarity index 62% rename from app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateResultGui.java rename to app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateResultGui.java index 653dbbd583..2aa2365eff 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSMAUpdateResultGui.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSMA/events/EventOpenAPSUpdateResultGui.java @@ -3,10 +3,10 @@ package info.nightscout.androidaps.plugins.OpenAPSMA.events; /** * Created by mike on 05.08.2016. */ -public class EventOpenAPSMAUpdateResultGui { +public class EventOpenAPSUpdateResultGui { public String text = null; - public EventOpenAPSMAUpdateResultGui(String text) { + public EventOpenAPSUpdateResultGui(String text) { this.text = text; } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java index 60f7c48696..d6f1e8c6bb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/Dialogs/WizardDialog.java @@ -38,10 +38,8 @@ import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.PumpEnactResult; import info.nightscout.androidaps.db.BgReading; -import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.interfaces.TempBasalsInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; -import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderFragment; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; import info.nightscout.client.data.NSProfile; @@ -49,7 +47,6 @@ import info.nightscout.utils.BolusWizard; import info.nightscout.utils.DateUtil; import info.nightscout.utils.DecimalFormatter; import info.nightscout.utils.PlusMinusEditText; -import info.nightscout.utils.Round; import info.nightscout.utils.SafeParse; import info.nightscout.utils.ToastUtils; diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/persistentnotification/PersistentNotificationPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/persistentnotification/PersistentNotificationPlugin.java index dffe461ead..652e107d8c 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/persistentnotification/PersistentNotificationPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/persistentnotification/PersistentNotificationPlugin.java @@ -28,7 +28,6 @@ import info.nightscout.androidaps.events.EventTreatmentChange; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; -import info.nightscout.androidaps.plugins.Overview.Notification; import info.nightscout.client.data.NSProfile; import info.nightscout.utils.DecimalFormatter; diff --git a/app/src/main/java/info/nightscout/utils/BolusWizard.java b/app/src/main/java/info/nightscout/utils/BolusWizard.java index 736958a862..fa7ddf745d 100644 --- a/app/src/main/java/info/nightscout/utils/BolusWizard.java +++ b/app/src/main/java/info/nightscout/utils/BolusWizard.java @@ -3,7 +3,6 @@ package info.nightscout.utils; import org.json.JSONObject; import info.nightscout.androidaps.MainApp; -import info.nightscout.androidaps.R; import info.nightscout.androidaps.interfaces.TempBasalsInterface; import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; From 3dfac49b82b2ab0d5d70de5e9538351452543ce8 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Tue, 3 Jan 2017 21:15:40 +0100 Subject: [PATCH 02/51] duplicate MA -> AMA --- .../main/assets/OpenAPSAMA/determine-basal.js | 316 +++++++++++++++++ .../info/nightscout/androidaps/MainApp.java | 2 + .../DetermineBasalAdapterAMAJS.java | 324 ++++++++++++++++++ .../OpenAPSAMA/DetermineBasalResultAMA.java | 123 +++++++ .../OpenAPSAMA/OpenAPSAMAFragment.java | 141 ++++++++ .../plugins/OpenAPSAMA/OpenAPSAMAPlugin.java | 241 +++++++++++++ .../main/res/layout/openapsama_fragment.xml | 185 ++++++++++ app/src/main/res/values/strings.xml | 1 + 8 files changed, 1333 insertions(+) create mode 100644 app/src/main/assets/OpenAPSAMA/determine-basal.js create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAFragment.java create mode 100644 app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java create mode 100644 app/src/main/res/layout/openapsama_fragment.xml diff --git a/app/src/main/assets/OpenAPSAMA/determine-basal.js b/app/src/main/assets/OpenAPSAMA/determine-basal.js new file mode 100644 index 0000000000..4c5a00fade --- /dev/null +++ b/app/src/main/assets/OpenAPSAMA/determine-basal.js @@ -0,0 +1,316 @@ +/* + Determine Basal + + Released under MIT license. See the accompanying LICENSE.txt file for + full terms and conditions + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, offline, meal_data, setTempBasal) { + var rT = { //short for requestedTemp + }; + + if (typeof profile === 'undefined' || typeof profile.current_basal === 'undefined') { + rT.error ='Error: could not get current basal rate'; + return rT; + } + + var bg = glucose_status.glucose; + if (bg < 38) { //Dexcom is in ??? mode or calibrating, do nothing. Asked @benwest for raw data in iter_glucose + rT.error = "CGM is calibrating or in ??? state"; + return rT; + } + + var max_iob = profile.max_iob; // maximum amount of non-bolus IOB OpenAPS will ever deliver + + // if target_bg is set, great. otherwise, if min and max are set, then set target to their average + var target_bg; + if (typeof profile.target_bg !== 'undefined') { + target_bg = profile.target_bg; + } else { + if (typeof profile.min_bg !== 'undefined' && typeof profile.max_bg !== 'undefined') { + target_bg = (profile.min_bg + profile.max_bg) / 2; + } else { + rT.error ='Error: could not determine target_bg'; + return rT; + } + } + + + if (typeof iob_data === 'undefined' ) { + rT.error ='Error: iob_data undefined'; + return rT; + } + + if (typeof iob_data.activity === 'undefined' || typeof iob_data.iob === 'undefined' || typeof iob_data.activity === 'undefined') { + rT.error ='Error: iob_data missing some property'; + return rT; + } + + var tick; + + if (glucose_status.delta >= 0) { + tick = "+" + glucose_status.delta; + } else { + tick = glucose_status.delta; + } + var minDelta = Math.min(glucose_status.delta, glucose_status.avgdelta); + //var maxDelta = Math.max(glucose_status.delta, glucose_status.avgdelta); + + + //calculate BG impact: the amount BG "should" be rising or falling based on insulin activity alone + var bgi = Math.round(( -iob_data.activity * profile.sens * 5 )*100)/100; + // project positive deviations for 15 minutes + var deviation = Math.round( 15 / 5 * ( glucose_status.avgdelta - bgi ) ); + // project negative deviations for 30 minutes + if (deviation < 0) { + deviation = Math.round( 30 / 5 * ( glucose_status.avgdelta - bgi ) ); + } + //console.log("Avg.Delta: " + glucose_status.avgdelta.toFixed(1) + ", BGI: " + bgi.toFixed(1) + " 15m activity projection: " + deviation.toFixed(0)); + + // calculate the naive (bolus calculator math) eventual BG based on net IOB and sensitivity + var naive_eventualBG = Math.round( bg - (iob_data.iob * profile.sens) ); + // and adjust it for the deviation above + var eventualBG = naive_eventualBG + deviation; + // calculate what portion of that is due to bolussnooze + var bolusContrib = iob_data.bolussnooze * profile.sens; + // and add it back in to get snoozeBG, plus another 50% to avoid low-temping at mealtime + var naive_snoozeBG = Math.round( naive_eventualBG + 1.5 * bolusContrib ); + // adjust that for deviation like we did eventualBG + var snoozeBG = naive_snoozeBG + deviation; + + //console.log("BG: " + bg +"(" + tick + ","+glucose_status.avgdelta.toFixed(1)+")"+ " -> " + eventualBG + "-" + snoozeBG + " (Unadjusted: " + naive_eventualBG + "-" + naive_snoozeBG + "), BGI: " + bgi); + + var expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10; + //console.log("expectedDelta: " + expectedDelta); + + if (typeof eventualBG === 'undefined' || isNaN(eventualBG)) { + rT.error ='Error: could not calculate eventualBG'; + return rT; + } + + // min_bg of 90 -> threshold of 70, 110 -> 80, and 130 -> 90 + var threshold = profile.min_bg - 0.5*(profile.min_bg-50); + + rT = { + 'temp': 'absolute' + , 'bg': bg + , 'tick': tick + , 'eventualBG': eventualBG + , 'snoozeBG': snoozeBG + }; + + var basaliob; + if (iob_data.basaliob) { basaliob = iob_data.basaliob; } + else { basaliob = iob_data.iob - iob_data.bolussnooze; } + // allow meal assist to run when carbs are just barely covered + if (minDelta > Math.max(3, bgi) && ( (meal_data.carbs > 0 && (1.1 * meal_data.carbs/profile.carb_ratio > meal_data.boluses + basaliob)) || ( deviation > 25 && minDelta > 7 ) ) ) { + // ignore all covered IOB, and just set eventualBG to the current bg + eventualBG = Math.max(bg,eventualBG) + deviation; + rT.eventualBG = eventualBG; + profile.min_bg = 80; + target_bg = (profile.min_bg + profile.max_bg) / 2; + expectedDelta = Math.round(( bgi + ( target_bg - eventualBG ) / ( profile.dia * 60 / 5 ) )*10)/10; + rT.mealAssist = "On: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi; + } else { + rT.mealAssist = "Off: Carbs: " + meal_data.carbs + " Boluses: " + meal_data.boluses + " Target: " + target_bg + " Deviation: " + deviation + " BGI: " + bgi; + } + if (bg < threshold) { // low glucose suspend mode: BG is < ~80 + rT.reason = "BG " + bg + "<" + threshold; + if ((glucose_status.delta <= 0 && glucose_status.avgdelta <= 0) || (glucose_status.delta < expectedDelta && glucose_status.avgdelta < expectedDelta)) { + // BG is still falling / rising slower than predicted + return setTempBasal(0, 30, profile, rT, offline); + } + if (glucose_status.delta > glucose_status.avgdelta) { + rT.reason += ", delta " + glucose_status.delta + ">0"; + } else { + rT.reason += ", avg delta " + glucose_status.avgdelta.toFixed(2) + ">0"; + } + if (currenttemp.rate > profile.current_basal) { // if a high-temp is running + rT.reason += ", cancel high temp"; + return setTempBasal(0, 0, profile, rT, offline); // cancel high temp + } else if (currenttemp.duration && eventualBG > profile.max_bg) { // if low-temped and predicted to go high from negative IOB + rT.reason += ", cancel low temp"; + return setTempBasal(0, 0, profile, rT, offline); // cancel low temp + } + rT.reason += "; no high-temp to cancel"; + return rT; + } + if (eventualBG < profile.min_bg) { // if eventual BG is below target: + if (rT.mealAssist.indexOf("On") == 0) { + rT.reason = "Meal assist: " + meal_data.carbs + "g, " + meal_data.boluses + "U"; + } else { + rT.reason = "Eventual BG " + eventualBG + "<" + profile.min_bg; + // if 5m or 15m avg BG is rising faster than expected delta + if (minDelta > expectedDelta && minDelta > 0) { + if (glucose_status.delta > glucose_status.avgdelta) { + rT.reason += ", but Delta " + tick + " > Exp. Delta " + expectedDelta; + } else { + rT.reason += ", but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " > Exp. Delta " + expectedDelta; + } + if (currenttemp.duration > 0) { // if there is currently any temp basal running + rT.reason = rT.reason += "; cancel"; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } else { + rT.reason = rT.reason += "; no temp to cancel"; + return rT; + } + } + } + + if (eventualBG < profile.min_bg) { + // if this is just due to boluses, we can snooze until the bolus IOB decays (at double speed) + if (snoozeBG > profile.min_bg) { // if adding back in the bolus contribution BG would be above min + // if BG is falling and high-temped, or rising and low-temped, cancel + // compare against zero here, not BGI, because BGI will be highly negative from boluses and no carbs + if (glucose_status.delta < 0 && currenttemp.duration > 0 && currenttemp.rate > profile.current_basal) { + rT.reason += tick + ", and temp " + currenttemp.rate + " > basal " + profile.current_basal; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } else if (glucose_status.delta > 0 && currenttemp.duration > 0 && currenttemp.rate < profile.current_basal) { + rT.reason += tick + ", and temp " + currenttemp.rate + " < basal " + profile.current_basal; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } + + rT.reason += ", bolus snooze: eventual BG range " + eventualBG + "-" + snoozeBG; + return rT; + } else { + // calculate 30m low-temp required to get projected BG up to target + // use snoozeBG to more gradually ramp in any counteraction of the user's boluses + // multiply by 2 to low-temp faster for increased hypo safety + var insulinReq = 2 * Math.min(0, (snoozeBG - target_bg) / profile.sens); + if (minDelta < 0 && minDelta > expectedDelta) { + // if we're barely falling, newinsulinReq should be barely negative + rT.reason += ", Snooze BG " + snoozeBG; + var newinsulinReq = Math.round(( insulinReq * (minDelta / expectedDelta) ) * 100)/100; + //console.log("Increasing insulinReq from " + insulinReq + " to " + newinsulinReq); + insulinReq = newinsulinReq; + } + // rate required to deliver insulinReq less insulin over 30m: + var rate = profile.current_basal + (2 * insulinReq); + rate = Math.round( rate * 1000 ) / 1000; + // if required temp < existing temp basal + var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60; + if (insulinScheduled < insulinReq - 0.2) { // if current temp would deliver >0.2U less than the required insulin, raise the rate + rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " < req " + insulinReq + "-0.2U"; + return setTempBasal(rate, 30, profile, rT, offline); + } + if (typeof currenttemp.rate !== 'undefined' && (currenttemp.duration > 5 && rate > currenttemp.rate - 0.1)) { + rT.reason += ", temp " + currenttemp.rate + " ~< req " + rate + "U/hr"; + return rT; + } else { + rT.reason += ", setting " + rate + "U/hr"; + return setTempBasal(rate, 30, profile, rT, offline); + } + } + } + } + + // if eventual BG is above min but BG is falling faster than expected Delta + if (minDelta < expectedDelta) { + if (glucose_status.delta < glucose_status.avgdelta) { + rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Delta " + tick + " < Exp. Delta " + expectedDelta; + } else { + rT.reason = "Eventual BG " + eventualBG + ">" + profile.min_bg + " but Avg. Delta " + glucose_status.avgdelta.toFixed(2) + " < Exp. Delta " + expectedDelta; + } + if (currenttemp.duration > 0) { // if there is currently any temp basal running + rT.reason = rT.reason += "; cancel"; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } else { + rT.reason = rT.reason += "; no temp to cancel"; + return rT; + } + } + + if (eventualBG < profile.max_bg) { + rT.reason = eventualBG + " is in range. No temp required"; + if (currenttemp.duration > 0) { // if there is currently any temp basal running + rT.reason = rT.reason += "; cancel"; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } + if (offline == 'Offline') { + // if no temp is running or required, set the current basal as a temp, so you can see on the pump that the loop is working + if ((!currenttemp.duration || (currenttemp.rate == profile.current_basal)) && !rT.duration) { + rT.reason = rT.reason + "; setting current basal of " + profile.current_basal + " as temp"; + return setTempBasal(profile.current_basal, 30, profile, rT, offline); + } + } + return rT; + } + + if (snoozeBG < profile.max_bg) { + rT.reason = snoozeBG + " < " + profile.max_bg; + if (currenttemp.duration > 0) { // if there is currently any temp basal running + rT.reason = rT.reason += "; cancel"; + return setTempBasal(0, 0, profile, rT, offline); // cancel temp + } else { + rT.reason = rT.reason += "; no temp to cancel"; + return rT; + } + } + + + // eventual BG is at/above target: + // if iob is over max, just cancel any temps + var basaliob; + if (iob_data.basaliob) { basaliob = iob_data.basaliob; } + else { basaliob = iob_data.iob - iob_data.bolussnooze; } + rT.reason = "Eventual BG " + eventualBG + ">=" + profile.max_bg + ", "; + if (basaliob > max_iob) { + rT.reason = "basaliob " + basaliob + " > max_iob " + max_iob; + return setTempBasal(0, 0, profile, rT, offline); + } else { // otherwise, calculate 30m high-temp required to get projected BG down to target + + // insulinReq is the additional insulin required to get down to max bg: + // if in meal assist mode, check if snoozeBG is lower, as eventualBG is not dependent on IOB + var insulinReq = (Math.min(snoozeBG,eventualBG) - target_bg) / profile.sens; + if (minDelta < 0 && minDelta > expectedDelta) { + var newinsulinReq = Math.round(( insulinReq * (1 - (minDelta / expectedDelta)) ) * 100)/100; + //console.log("Reducing insulinReq from " + insulinReq + " to " + newinsulinReq); + insulinReq = newinsulinReq; + } + // if that would put us over max_iob, then reduce accordingly + if (insulinReq > max_iob-basaliob) { + rT.reason = "max_iob " + max_iob + ", "; + insulinReq = max_iob-basaliob; + } + + // rate required to deliver insulinReq more insulin over 30m: + var rate = profile.current_basal + (2 * insulinReq); + rate = Math.round( rate * 1000 ) / 1000; + + var maxSafeBasal = Math.min(profile.max_basal, 3 * profile.max_daily_basal, 4 * profile.current_basal); + if (rate > maxSafeBasal) { + rT.reason += "adj. req. rate:"+rate.toFixed(1) +" to maxSafeBasal:"+maxSafeBasal.toFixed(1)+", "; + rate = maxSafeBasal; + } + + var insulinScheduled = currenttemp.duration * (currenttemp.rate - profile.current_basal) / 60; + if (insulinScheduled > insulinReq + 0.2) { // if current temp would deliver >0.2U more than the required insulin, lower the rate + rT.reason = currenttemp.duration + "m@" + (currenttemp.rate - profile.current_basal).toFixed(3) + " = " + insulinScheduled.toFixed(3) + " > req " + insulinReq + "+0.2U"; + return setTempBasal(rate, 30, profile, rT, offline); + } + + if (typeof currenttemp.duration == 'undefined' || currenttemp.duration == 0) { // no temp is set + rT.reason += "no temp, setting " + rate + "U/hr"; + return setTempBasal(rate, 30, profile, rT, offline); + } + + if (currenttemp.duration > 5 && rate < currenttemp.rate + 0.1) { // if required temp <~ existing temp basal + rT.reason += "temp " + currenttemp.rate + " >~ req " + rate + "U/hr"; + return rT; + } + + // required temp > existing temp basal + rT.reason += "temp " + currenttemp.rate + "<" + rate + "U/hr"; + return setTempBasal(rate, 30, profile, rT, offline); + } + +}; + +module.exports = determine_basal; \ No newline at end of file diff --git a/app/src/main/java/info/nightscout/androidaps/MainApp.java b/app/src/main/java/info/nightscout/androidaps/MainApp.java index 2d7ebbde2c..13ab45c10e 100644 --- a/app/src/main/java/info/nightscout/androidaps/MainApp.java +++ b/app/src/main/java/info/nightscout/androidaps/MainApp.java @@ -29,6 +29,7 @@ import info.nightscout.androidaps.plugins.Loop.LoopFragment; import info.nightscout.androidaps.plugins.MDI.MDIFragment; import info.nightscout.androidaps.plugins.NSProfile.NSProfileFragment; import info.nightscout.androidaps.plugins.Objectives.ObjectivesFragment; +import info.nightscout.androidaps.plugins.OpenAPSAMA.OpenAPSAMAFragment; import info.nightscout.androidaps.plugins.OpenAPSMA.OpenAPSMAFragment; import info.nightscout.androidaps.plugins.Overview.OverviewFragment; import info.nightscout.androidaps.plugins.SafetyFragment.SafetyFragment; @@ -83,6 +84,7 @@ public class MainApp extends Application { pluginsList.add(VirtualPumpFragment.getPlugin()); if (Config.LOOPENABLED) pluginsList.add(LoopFragment.getPlugin()); if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSMAFragment.getPlugin()); + if (Config.OPENAPSENABLED) pluginsList.add(OpenAPSAMAFragment.getPlugin()); pluginsList.add(NSProfileFragment.getPlugin()); pluginsList.add(SimpleProfileFragment.getPlugin()); pluginsList.add(LocalProfileFragment.getPlugin()); diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java new file mode 100644 index 0000000000..dc314cb2e7 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalAdapterAMAJS.java @@ -0,0 +1,324 @@ +package info.nightscout.androidaps.plugins.OpenAPSAMA; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.eclipsesource.v8.JavaVoidCallback; +import com.eclipsesource.v8.V8; +import com.eclipsesource.v8.V8Array; +import com.eclipsesource.v8.V8Object; + +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.db.DatabaseHelper; +import info.nightscout.androidaps.plugins.Loop.ScriptReader; +import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.client.data.NSProfile; + +public class DetermineBasalAdapterAMAJS implements Parcelable { + private static Logger log = LoggerFactory.getLogger(DetermineBasalAdapterAMAJS.class); + + + private ScriptReader mScriptReader = null; + V8 mV8rt; + private V8Object mProfile; + private V8Object mGlucoseStatus; + private V8Object mIobData; + private V8Object mMealData; + private V8Object mCurrentTemp; + + private final String PARAM_currentTemp = "currentTemp"; + private final String PARAM_iobData = "iobData"; + private final String PARAM_glucoseStatus = "glucose_status"; + private final String PARAM_profile = "profile"; + private final String PARAM_meal_data = "meal_data"; + + private String storedCurrentTemp = null; + public String storedIobData = null; + private String storedGlucoseStatus = null; + private String storedProfile = null; + private String storedMeal_data = null; + + /** + * Parcelable implementation + * result string for display only + **/ + protected DetermineBasalAdapterAMAJS(Parcel in) { + storedCurrentTemp = in.readString(); + storedIobData = in.readString(); + storedGlucoseStatus = in.readString(); + storedProfile = in.readString(); + storedMeal_data = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(storedCurrentTemp); + dest.writeString(storedIobData); + dest.writeString(storedGlucoseStatus); + dest.writeString(storedProfile); + dest.writeString(storedMeal_data); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public DetermineBasalAdapterAMAJS createFromParcel(Parcel in) { + return new DetermineBasalAdapterAMAJS(in); + } + + @Override + public DetermineBasalAdapterAMAJS[] newArray(int size) { + return new DetermineBasalAdapterAMAJS[size]; + } + }; + + /** + * Main code + */ + + public DetermineBasalAdapterAMAJS(ScriptReader scriptReader) throws IOException { + mV8rt = V8.createV8Runtime(); + mScriptReader = scriptReader; + + init(); + initLogCallback(); + initProcessExitCallback(); + initModuleParent(); + loadScript(); + } + + public void init() { + // Profile + mProfile = new V8Object(mV8rt); + mProfile.add("max_iob", 0); + mProfile.add("carbs_hr", 0); + mProfile.add("dia", 0); + mProfile.add("type", "current"); + mProfile.add("max_daily_basal", 0); + mProfile.add("max_basal", 0); + mProfile.add("max_bg", 0); + mProfile.add("min_bg", 0); + mProfile.add("carbratio", 0); + mProfile.add("sens", 0); + mProfile.add("current_basal", 0); + mV8rt.add(PARAM_profile, mProfile); + // Current temp + mCurrentTemp = new V8Object(mV8rt); + mCurrentTemp.add("temp", "absolute"); + mCurrentTemp.add("duration", 0); + mCurrentTemp.add("rate", 0); + mV8rt.add(PARAM_currentTemp, mCurrentTemp); + // IOB data + mIobData = new V8Object(mV8rt); + mIobData.add("iob", 0); //netIob + mIobData.add("activity", 0); //netActivity + mIobData.add("bolussnooze", 0); //bolusIob + mIobData.add("basaliob", 0); + mIobData.add("netbasalinsulin", 0); + mIobData.add("hightempinsulin", 0); + mV8rt.add(PARAM_iobData, mIobData); + // Glucose status + mGlucoseStatus = new V8Object(mV8rt); + mGlucoseStatus.add("glucose", 0); + mGlucoseStatus.add("delta", 0); + mGlucoseStatus.add("avgdelta", 0); + mV8rt.add(PARAM_glucoseStatus, mGlucoseStatus); + // Meal data + mMealData = new V8Object(mV8rt); + mMealData.add("carbs", 0); + mMealData.add("boluses", 0); + mV8rt.add(PARAM_meal_data, mMealData); + } + + public DetermineBasalResultAMA invoke() { + mV8rt.executeVoidScript( + "console.error(\"determine_basal(\"+\n" + + "JSON.stringify(" + PARAM_glucoseStatus + ")+ \", \" +\n" + + "JSON.stringify(" + PARAM_currentTemp + ")+ \", \" +\n" + + "JSON.stringify(" + PARAM_iobData + ")+ \", \" +\n" + + "JSON.stringify(" + PARAM_profile + ")+ \", \" +\n" + + "JSON.stringify(" + PARAM_meal_data + ")+ \") \");" + ); + mV8rt.executeVoidScript( + "var rT = determine_basal(" + + PARAM_glucoseStatus + ", " + + PARAM_currentTemp + ", " + + PARAM_iobData + ", " + + PARAM_profile + ", " + + "undefined, " + + PARAM_meal_data + ", " + + "setTempBasal" + + ");"); + + + String ret = mV8rt.executeStringScript("JSON.stringify(rT);"); + if (Config.logAPSResult) + log.debug("Result: " + ret); + + V8Object v8ObjectReuslt = mV8rt.getObject("rT"); + + DetermineBasalResultAMA result = null; + try { + result = new DetermineBasalResultAMA(v8ObjectReuslt, new JSONObject(ret)); + } catch (JSONException e) { + e.printStackTrace(); + } + + // Store input params for Parcelable + storedGlucoseStatus = mV8rt.executeStringScript("JSON.stringify(" + PARAM_glucoseStatus + ");"); + storedIobData = mV8rt.executeStringScript("JSON.stringify(" + PARAM_iobData + ");"); + storedCurrentTemp = mV8rt.executeStringScript("JSON.stringify(" + PARAM_currentTemp + ");"); + storedProfile = mV8rt.executeStringScript("JSON.stringify(" + PARAM_profile + ");"); + storedMeal_data = mV8rt.executeStringScript("JSON.stringify(" + PARAM_meal_data + ");"); + + return result; + } + + String getGlucoseStatusParam() { + return storedGlucoseStatus; + } + + String getCurrentTempParam() { + return storedCurrentTemp; + } + + String getIobDataParam() { + return storedIobData; + } + + String getProfileParam() { + return storedProfile; + } + + String getMealDataParam() { + return storedMeal_data; + } + + private void loadScript() throws IOException { + mV8rt.executeVoidScript( + readFile("OpenAPSAMA/determine-basal.js"), + "OpenAPSAMA/bin/oref0-determine-basal.js", + 0); + mV8rt.executeVoidScript("var determine_basal = module.exports;"); + mV8rt.executeVoidScript( + "var setTempBasal = function (rate, duration, profile, rT, offline) {" + + "rT.duration = duration;\n" + + " rT.rate = rate;" + + "return rT;" + + "};", + "setTempBasal.js", + 0 + ); + } + + private void initModuleParent() { + mV8rt.executeVoidScript("var module = {\"parent\":Boolean(1)};"); + } + + private void initProcessExitCallback() { + JavaVoidCallback callbackProccessExit = new JavaVoidCallback() { + @Override + public void invoke(V8Object arg0, V8Array parameters) { + if (parameters.length() > 0) { + Object arg1 = parameters.get(0); + log.error("ProccessExit " + arg1); + } + } + }; + mV8rt.registerJavaMethod(callbackProccessExit, "proccessExit"); + mV8rt.executeVoidScript("var process = {\"exit\": function () { proccessExit(); } };"); + } + + private void initLogCallback() { + JavaVoidCallback callbackLog = new JavaVoidCallback() { + @Override + public void invoke(V8Object arg0, V8Array parameters) { + if (parameters.length() > 0) { + Object arg1 = parameters.get(0); + if (Config.logAPSResult) + log.debug("Input params: " + arg1); + } + } + }; + mV8rt.registerJavaMethod(callbackLog, "log"); + mV8rt.executeVoidScript("var console = {\"log\":log, \"error\":log};"); + } + + + public void setData(NSProfile profile, + double maxIob, + double maxBasal, + double minBg, + double maxBg, + double targetBg, + PumpInterface pump, + IobTotal iobData, + DatabaseHelper.GlucoseStatus glucoseStatus, + TreatmentsPlugin.MealData mealData) { + + String units = profile.getUnits(); + + mProfile.add("max_iob", maxIob); + mProfile.add("carbs_hr", profile.getCarbAbsorbtionRate()); + mProfile.add("dia", profile.getDia()); + mProfile.add("type", "current"); + mProfile.add("max_daily_basal", profile.getMaxDailyBasal()); + mProfile.add("max_basal", maxBasal); + mProfile.add("min_bg", minBg); + mProfile.add("max_bg", maxBg); + mProfile.add("target_bg", targetBg); + mProfile.add("carbratio", profile.getIc(profile.secondsFromMidnight())); + mProfile.add("sens", NSProfile.toMgdl(profile.getIsf(NSProfile.secondsFromMidnight()).doubleValue(), units)); + + mProfile.add("current_basal", pump.getBaseBasalRate()); + mCurrentTemp.add("duration", pump.getTempBasalRemainingMinutes()); + mCurrentTemp.add("rate", pump.getTempBasalAbsoluteRate()); + + mIobData.add("iob", iobData.iob); //netIob + mIobData.add("activity", iobData.activity); //netActivity + mIobData.add("bolussnooze", iobData.bolussnooze); //bolusIob + mIobData.add("basaliob", iobData.basaliob); + mIobData.add("netbasalinsulin", iobData.netbasalinsulin); + mIobData.add("hightempinsulin", iobData.hightempinsulin); + + mGlucoseStatus.add("glucose", glucoseStatus.glucose); + mGlucoseStatus.add("delta", glucoseStatus.delta); + mGlucoseStatus.add("avgdelta", glucoseStatus.avgdelta); + + mMealData.add("carbs", mealData.carbs); + mMealData.add("boluses", mealData.boluses); + } + + + public void release() { + mProfile.release(); + mCurrentTemp.release(); + mIobData.release(); + mMealData.release(); + mGlucoseStatus.release(); + mV8rt.release(); + } + + public String readFile(String filename) throws IOException { + byte[] bytes = mScriptReader.readFile(filename); + String string = new String(bytes, "UTF-8"); + if (string.startsWith("#!/usr/bin/env node")) { + string = string.substring(20); + } + return string; + } + +} 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 new file mode 100644 index 0000000000..5accd36ff6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/DetermineBasalResultAMA.java @@ -0,0 +1,123 @@ +package info.nightscout.androidaps.plugins.OpenAPSAMA; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.eclipsesource.v8.V8Object; + +import org.json.JSONException; +import org.json.JSONObject; + +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; + +public class DetermineBasalResultAMA extends APSResult { + + public JSONObject json = new JSONObject(); + public double eventualBG; + public double snoozeBG; + public String mealAssist; + public IobTotal iob; + + public DetermineBasalResultAMA(V8Object result, JSONObject j) { + json = j; + if (result.contains("error")) { + reason = result.getString("error"); + changeRequested = false; + rate = -1; + duration = -1; + mealAssist = ""; + } else { + reason = result.getString("reason"); + eventualBG = result.getDouble("eventualBG"); + snoozeBG = result.getDouble("snoozeBG"); + if (result.contains("rate")) { + rate = result.getDouble("rate"); + if (rate < 0d) rate = 0d; + changeRequested = true; + } else { + rate = -1; + changeRequested = false; + } + if (result.contains("duration")) { + duration = result.getInteger("duration"); + changeRequested = changeRequested; + } else { + duration = -1; + changeRequested = false; + } + if (result.contains("mealAssist")) { + mealAssist = result.getString("mealAssist"); + } else mealAssist = ""; + } + result.release(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(json.toString()); + dest.writeDouble(eventualBG); + dest.writeDouble(snoozeBG); + dest.writeString(mealAssist); + } + + public final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DetermineBasalResultAMA createFromParcel(Parcel in) { + return new DetermineBasalResultAMA(in); + } + + public DetermineBasalResultAMA[] newArray(int size) { + return new DetermineBasalResultAMA[size]; + } + }; + + private DetermineBasalResultAMA(Parcel in) { + super(in); + try { + json = new JSONObject(in.readString()); + } catch (JSONException e) { + e.printStackTrace(); + } + eventualBG = in.readDouble(); + snoozeBG = in.readDouble(); + mealAssist = in.readString(); + } + + public DetermineBasalResultAMA() { + } + + @Override + public DetermineBasalResultAMA clone() { + DetermineBasalResultAMA newResult = new DetermineBasalResultAMA(); + newResult.reason = new String(reason); + newResult.rate = rate; + newResult.duration = duration; + newResult.changeRequested = changeRequested; + newResult.rate = rate; + newResult.duration = duration; + newResult.changeRequested = changeRequested; + + try { + newResult.json = new JSONObject(json.toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + newResult.eventualBG = eventualBG; + newResult.snoozeBG = snoozeBG; + newResult.mealAssist = new String(mealAssist); + return newResult; + } + + @Override + public JSONObject json() { + try { + JSONObject ret = new JSONObject(this.json.toString()); + return ret; + } catch (JSONException e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAFragment.java new file mode 100644 index 0000000000..0f6219cc41 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAFragment.java @@ -0,0 +1,141 @@ +package info.nightscout.androidaps.plugins.OpenAPSAMA; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import com.squareup.otto.Subscribe; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.interfaces.FragmentBase; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.utils.JSONFormatter; + +public class OpenAPSAMAFragment extends Fragment implements View.OnClickListener, FragmentBase { + private static Logger log = LoggerFactory.getLogger(OpenAPSAMAFragment.class); + + private static OpenAPSAMAPlugin openAPSAMAPlugin; + + public static OpenAPSAMAPlugin getPlugin() { + if(openAPSAMAPlugin ==null){ + openAPSAMAPlugin = new OpenAPSAMAPlugin(); + } + return openAPSAMAPlugin; + } + + Button run; + TextView lastRunView; + TextView glucoseStatusView; + TextView currentTempView; + TextView iobDataView; + TextView profileView; + TextView mealDataView; + TextView resultView; + TextView requestView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.openapsama_fragment, container, false); + + run = (Button) view.findViewById(R.id.openapsma_run); + run.setOnClickListener(this); + lastRunView = (TextView) view.findViewById(R.id.openapsma_lastrun); + glucoseStatusView = (TextView) view.findViewById(R.id.openapsma_glucosestatus); + currentTempView = (TextView) view.findViewById(R.id.openapsma_currenttemp); + iobDataView = (TextView) view.findViewById(R.id.openapsma_iobdata); + profileView = (TextView) view.findViewById(R.id.openapsma_profile); + mealDataView = (TextView) view.findViewById(R.id.openapsma_mealdata); + resultView = (TextView) view.findViewById(R.id.openapsma_result); + requestView = (TextView) view.findViewById(R.id.openapsma_request); + + updateGUI(); + return view; + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.openapsma_run: + getPlugin().invoke(); + break; + } + + } + + @Override + public void onPause() { + super.onPause(); + MainApp.bus().unregister(this); + } + + @Override + public void onResume() { + super.onResume(); + MainApp.bus().register(this); + } + + @Subscribe + public void onStatusEvent(final EventOpenAPSUpdateGui ev) { + updateGUI(); + } + + @Subscribe + public void onStatusEvent(final EventOpenAPSUpdateResultGui ev) { + updateResultGUI(ev.text); + } + + void updateGUI() { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + DetermineBasalResultAMA lastAPSResult = getPlugin().lastAPSResult; + if (lastAPSResult != null) { + resultView.setText(JSONFormatter.format(lastAPSResult.json)); + requestView.setText(lastAPSResult.toSpanned()); + } + DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS = getPlugin().lastDetermineBasalAdapterAMAJS; + if (determineBasalAdapterAMAJS != null) { + glucoseStatusView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getGlucoseStatusParam())); + currentTempView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getCurrentTempParam())); + iobDataView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getIobDataParam())); + profileView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getProfileParam())); + mealDataView.setText(JSONFormatter.format(determineBasalAdapterAMAJS.getMealDataParam())); + } + if (getPlugin().lastAPSRun != null) { + lastRunView.setText(getPlugin().lastAPSRun.toLocaleString()); + } + } + }); + } + + void updateResultGUI(final String text) { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + resultView.setText(text); + glucoseStatusView.setText(""); + currentTempView.setText(""); + iobDataView.setText(""); + profileView.setText(""); + mealDataView.setText(""); + requestView.setText(""); + lastRunView.setText(""); + } + }); + } +} 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 new file mode 100644 index 0000000000..b666465a2e --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/OpenAPSAMAPlugin.java @@ -0,0 +1,241 @@ +package info.nightscout.androidaps.plugins.OpenAPSAMA; + +import android.content.SharedPreferences; +import android.preference.PreferenceManager; + +import org.json.JSONException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Date; + +import info.nightscout.androidaps.Config; +import info.nightscout.androidaps.Constants; +import info.nightscout.androidaps.MainApp; +import info.nightscout.androidaps.R; +import info.nightscout.androidaps.db.DatabaseHelper; +import info.nightscout.androidaps.interfaces.APSInterface; +import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.PumpInterface; +import info.nightscout.androidaps.interfaces.TempBasalsInterface; +import info.nightscout.androidaps.interfaces.TreatmentsInterface; +import info.nightscout.androidaps.plugins.Loop.APSResult; +import info.nightscout.androidaps.plugins.Loop.ScriptReader; +import info.nightscout.androidaps.plugins.OpenAPSMA.IobTotal; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; +import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.client.data.NSProfile; +import info.nightscout.utils.DateUtil; +import info.nightscout.utils.Round; +import info.nightscout.utils.SafeParse; +import info.nightscout.utils.ToastUtils; + +/** + * Created by mike on 05.08.2016. + */ +public class OpenAPSAMAPlugin implements PluginBase, APSInterface { + private static Logger log = LoggerFactory.getLogger(OpenAPSAMAPlugin.class); + + // last values + DetermineBasalAdapterAMAJS lastDetermineBasalAdapterAMAJS = null; + Date lastAPSRun = null; + DetermineBasalResultAMA lastAPSResult = null; + + boolean fragmentEnabled = false; + boolean fragmentVisible = true; + + @Override + public String getName() { + return MainApp.instance().getString(R.string.openapsama); + } + + @Override + public boolean isEnabled(int type) { + return type == APS && fragmentEnabled && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; + } + + @Override + public boolean isVisibleInTabs(int type) { + return type == APS && fragmentVisible && MainApp.getConfigBuilder().getPumpDescription().isTempBasalCapable; + } + + @Override + public boolean canBeHidden(int type) { + return true; + } + + @Override + public void setFragmentVisible(int type, boolean fragmentVisible) { + if (type == APS) this.fragmentVisible = fragmentVisible; + } + + @Override + public void setFragmentEnabled(int type, boolean fragmentEnabled) { + if (type == APS) this.fragmentEnabled = fragmentEnabled; + } + + @Override + public int getType() { + return PluginBase.APS; + } + + @Override + public String getFragmentClass() { + return OpenAPSAMAFragment.class.getName(); + } + + @Override + public APSResult getLastAPSResult() { + return lastAPSResult; + } + + @Override + public Date getLastAPSRun() { + return lastAPSRun; + } + + @Override + public void invoke() { + lastAPSResult = null; + DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS = null; + try { + determineBasalAdapterAMAJS = new DetermineBasalAdapterAMAJS(new ScriptReader(MainApp.instance().getBaseContext())); + } catch (IOException e) { + log.error(e.getMessage(), e); + return; + } + + DatabaseHelper.GlucoseStatus glucoseStatus = MainApp.getDbHelper().getGlucoseStatusData(); + NSProfile profile = MainApp.getConfigBuilder().getActiveProfile().getProfile(); + PumpInterface pump = MainApp.getConfigBuilder(); + + if (!isEnabled(PluginBase.APS)) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_disabled))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_disabled)); + return; + } + + if (glucoseStatus == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noglucosedata))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_noglucosedata)); + return; + } + + if (profile == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_noprofile))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_noprofile)); + return; + } + + if (pump == null) { + MainApp.bus().post(new EventOpenAPSUpdateResultGui(MainApp.instance().getString(R.string.openapsma_nopump))); + if (Config.logAPSResult) + log.debug(MainApp.instance().getString(R.string.openapsma_nopump)); + return; + } + + SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(MainApp.instance().getApplicationContext()); + String units = profile.getUnits(); + + String maxBgDefault = "180"; + String minBgDefault = "100"; + String targetBgDefault = "150"; + if (!units.equals(Constants.MGDL)) { + maxBgDefault = "10"; + minBgDefault = "5"; + targetBgDefault = "7"; + } + + Date now = new Date(); + + double maxIob = SafeParse.stringToDouble(SP.getString("openapsma_max_iob", "1.5")); + double maxBasal = SafeParse.stringToDouble(SP.getString("openapsma_max_basal", "1")); + double minBg = NSProfile.toMgdl(SafeParse.stringToDouble(SP.getString("openapsma_min_bg", minBgDefault)), units); + double maxBg = NSProfile.toMgdl(SafeParse.stringToDouble(SP.getString("openapsma_max_bg", maxBgDefault)), units); + double targetBg = NSProfile.toMgdl(SafeParse.stringToDouble(SP.getString("openapsma_target_bg", targetBgDefault)), units); + + minBg = Round.roundTo(minBg, 0.1d); + maxBg = Round.roundTo(maxBg, 0.1d); + + TreatmentsInterface treatments = MainApp.getConfigBuilder().getActiveTreatments(); + TempBasalsInterface tempBasals = MainApp.getConfigBuilder().getActiveTempBasals(); + treatments.updateTotalIOB(); + tempBasals.updateTotalIOB(); + IobTotal bolusIob = treatments.getLastCalculation(); + IobTotal basalIob = tempBasals.getLastCalculation(); + + IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); + + TreatmentsPlugin.MealData mealData = treatments.getMealData(); + + maxIob = MainApp.getConfigBuilder().applyMaxIOBConstraints(maxIob); + + minBg = verifyHardLimits(minBg, "minBg", 72, 180); + maxBg = verifyHardLimits(maxBg, "maxBg", 100, 270); + targetBg = verifyHardLimits(targetBg, "targetBg", 80, 200); + maxIob = verifyHardLimits(maxIob, "maxIob", 0, 7); + maxBasal = verifyHardLimits(maxBasal, "max_basal", 0.1, 10); + + if (!checkOnlyHardLimits(profile.getCarbAbsorbtionRate(), "carbs_hr", 4, 100)) return; + if (!checkOnlyHardLimits(profile.getDia(), "dia", 2, 7)) return; + if (!checkOnlyHardLimits(profile.getIc(profile.secondsFromMidnight()), "carbratio", 2, 100)) return; + if (!checkOnlyHardLimits(NSProfile.toMgdl(profile.getIsf(NSProfile.secondsFromMidnight()).doubleValue(), units), "sens", 2, 900)) return; + if (!checkOnlyHardLimits(profile.getMaxDailyBasal(), "max_daily_basal", 0.1, 10)) return; + if (!checkOnlyHardLimits(pump.getBaseBasalRate(), "current_basal", 0.01, 5)) return; + + determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobTotal, glucoseStatus, mealData); + + + DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke(); + // Fix bug determine basal + if (determineBasalResultAMA.rate == 0d && determineBasalResultAMA.duration == 0 && !MainApp.getConfigBuilder().isTempBasalInProgress()) + determineBasalResultAMA.changeRequested = false; + // limit requests on openloop mode + if (!MainApp.getConfigBuilder().isClosedModeEnabled()) { + if (MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultAMA.rate - MainApp.getConfigBuilder().getTempBasalAbsoluteRate()) < 0.1) + determineBasalResultAMA.changeRequested = false; + if (!MainApp.getConfigBuilder().isTempBasalInProgress() && Math.abs(determineBasalResultAMA.rate - MainApp.getConfigBuilder().getBaseBasalRate()) < 0.1) + determineBasalResultAMA.changeRequested = false; + } + + determineBasalResultAMA.iob = iobTotal; + + determineBasalAdapterAMAJS.release(); + + try { + determineBasalResultAMA.json.put("timestamp", DateUtil.toISOString(now)); + } catch (JSONException e) { + e.printStackTrace(); + } + + lastDetermineBasalAdapterAMAJS = determineBasalAdapterAMAJS; + lastAPSResult = determineBasalResultAMA; + lastAPSRun = now; + MainApp.bus().post(new EventOpenAPSUpdateGui()); + + //deviceStatus.suggested = determineBasalResultAMA.json; + } + + // safety checks + public static boolean checkOnlyHardLimits(Double value, String valueName, double lowLimit, double highLimit) { + return value.equals(verifyHardLimits(value, valueName, lowLimit, highLimit)); + } + + public static Double verifyHardLimits(Double value, String valueName, double lowLimit, double highLimit) { + if (value < lowLimit || value > highLimit) { + String msg = String.format(MainApp.sResources.getString(R.string.openapsma_valueoutofrange), valueName); + log.error(msg); + MainApp.getConfigBuilder().uploadError(msg); + ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), msg, R.raw.error); + value = Math.max(value, lowLimit); + value = Math.min(value, highLimit); + } + return value; + } + +} diff --git a/app/src/main/res/layout/openapsama_fragment.xml b/app/src/main/res/layout/openapsama_fragment.xml new file mode 100644 index 0000000000..b9069cbafd --- /dev/null +++ b/app/src/main/res/layout/openapsama_fragment.xml @@ -0,0 +1,185 @@ + + + + + + + + +