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 java.util.List; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; import info.nightscout.androidaps.data.GlucoseStatus; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.interfaces.PumpInterface; import info.nightscout.androidaps.plugins.Loop.APSResult; import info.nightscout.androidaps.plugins.Loop.ScriptReader; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateGui; import info.nightscout.androidaps.plugins.OpenAPSMA.events.EventOpenAPSUpdateResultGui; import info.nightscout.androidaps.plugins.TempTargetRange.TempTargetRangePlugin; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; import info.nightscout.utils.DateUtil; import info.nightscout.utils.Profiler; import info.nightscout.utils.Round; import info.nightscout.utils.SP; 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; AutosensResult lastAutosensResult = null; boolean fragmentEnabled = false; boolean fragmentVisible = true; @Override public String getName() { return MainApp.instance().getString(R.string.openapsama); } @Override public String getNameShort() { String name = MainApp.sResources.getString(R.string.oaps_shortname); if (!name.trim().isEmpty()){ //only if translation exists return name; } // use long name as fallback return getName(); } @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(String initiator) { log.debug("invoke from " + initiator); lastAPSResult = null; DetermineBasalAdapterAMAJS determineBasalAdapterAMAJS = null; try { determineBasalAdapterAMAJS = new DetermineBasalAdapterAMAJS(new ScriptReader(MainApp.instance().getBaseContext())); } catch (IOException e) { log.error(e.getMessage(), e); return; } GlucoseStatus glucoseStatus = GlucoseStatus.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; } String units = profile.getUnits(); Double maxBgDefault = Constants.MAX_BG_DEFAULT_MGDL; Double minBgDefault = Constants.MIN_BG_DEFAULT_MGDL; Double targetBgDefault = Constants.TARGET_BG_DEFAULT_MGDL; if (!units.equals(Constants.MGDL)) { maxBgDefault = Constants.MAX_BG_DEFAULT_MMOL; minBgDefault = Constants.MIN_BG_DEFAULT_MMOL; targetBgDefault = Constants.TARGET_BG_DEFAULT_MMOL; } Date now = new Date(); double maxIob = SP.getDouble("openapsma_max_iob", 1.5d); double maxBasal = SP.getDouble("openapsma_max_basal", 1d); double minBg = NSProfile.toMgdl(SP.getDouble("openapsma_min_bg", minBgDefault), units); double maxBg = NSProfile.toMgdl(SP.getDouble("openapsma_max_bg", maxBgDefault), units); double targetBg = NSProfile.toMgdl(SP.getDouble("openapsma_target_bg", targetBgDefault), units); minBg = Round.roundTo(minBg, 0.1d); maxBg = Round.roundTo(maxBg, 0.1d); Date start = new Date(); Date startPart = new Date(); IobTotal[] iobArray = IobTotal.calculateIobArrayInDia(); Profiler.log(log, "calculateIobArrayInDia()", startPart); startPart = new Date(); MealData mealData = MainApp.getConfigBuilder().getActiveTreatments().getMealData(); Profiler.log(log, "getMealData()", startPart); maxIob = MainApp.getConfigBuilder().applyMaxIOBConstraints(maxIob); minBg = verifyHardLimits(minBg, "minBg", Constants.VERY_HARD_LIMIT_MIN_BG[0], Constants.VERY_HARD_LIMIT_MIN_BG[1]); maxBg = verifyHardLimits(maxBg, "maxBg", Constants.VERY_HARD_LIMIT_MAX_BG[0], Constants.VERY_HARD_LIMIT_MAX_BG[1]); targetBg = verifyHardLimits(targetBg, "targetBg", Constants.VERY_HARD_LIMIT_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TARGET_BG[1]); boolean isTempTarget = false; TempTargetRangePlugin tempTargetRangePlugin = (TempTargetRangePlugin) MainApp.getSpecificPlugin(TempTargetRangePlugin.class); if (tempTargetRangePlugin != null && tempTargetRangePlugin.isEnabled(PluginBase.GENERAL)) { TempTarget tempTarget = tempTargetRangePlugin.getTempTargetInProgress(new Date().getTime()); if (tempTarget != null) { isTempTarget = true; minBg = verifyHardLimits(tempTarget.low, "minBg", Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MIN_BG[1]); maxBg = verifyHardLimits(tempTarget.high, "maxBg", Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[0], Constants.VERY_HARD_LIMIT_TEMP_MAX_BG[1]); targetBg = verifyHardLimits((tempTarget.low + tempTarget.high) / 2, "targetBg", Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[0], Constants.VERY_HARD_LIMIT_TEMP_TARGET_BG[1]); } } maxIob = verifyHardLimits(maxIob, "maxIob", 0, 7); maxBasal = verifyHardLimits(maxBasal, "max_basal", 0.1, 10); 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; startPart = new Date(); long oldestDataAvailable = MainApp.getConfigBuilder().getActiveTempBasals().oldestDataAvaialable(); List<BgReading> bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime(Math.max(oldestDataAvailable, (long) (new Date().getTime() - 60 * 60 * 1000L * (24 + profile.getDia()))), false); log.debug("Limiting data to oldest available temps: " + new Date(oldestDataAvailable).toString() + " (" + bgReadings.size() + " records)"); Profiler.log(log, "getBgreadingsDataFromTime()", startPart); startPart = new Date(); if(MainApp.getConfigBuilder().isAMAModeEnabled()){ lastAutosensResult = Autosens.detectSensitivityandCarbAbsorption(bgReadings, null); } else { lastAutosensResult = new AutosensResult(); } Profiler.log(log, "detectSensitivityandCarbAbsorption()", startPart); Profiler.log(log, "AMA data gathering", start); start = new Date(); determineBasalAdapterAMAJS.setData(profile, maxIob, maxBasal, minBg, maxBg, targetBg, pump, iobArray, glucoseStatus, mealData, lastAutosensResult.ratio, //autosensDataRatio isTempTarget, SafeParse.stringToDouble(SP.getString("openapsama_min_5m_carbimpact", "3.0"))//min_5m_carbimpact ); DetermineBasalResultAMA determineBasalResultAMA = determineBasalAdapterAMAJS.invoke(); Profiler.log(log, "AMA calculation", start); // 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 = iobArray[0]; 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) { Double newvalue = value; if (newvalue < lowLimit || newvalue > highLimit) { newvalue = Math.max(newvalue, lowLimit); newvalue = Math.min(newvalue, highLimit); String msg = String.format(MainApp.sResources.getString(R.string.openapsma_valueoutofrange), valueName); msg += ".\n"; msg += String.format(MainApp.sResources.getString(R.string.openapsma_valuelimitedto), value, newvalue); log.error(msg); MainApp.getConfigBuilder().uploadError(msg); ToastUtils.showToastInUiThread(MainApp.instance().getApplicationContext(), msg, R.raw.error); } return newvalue; } }