diff --git a/app/src/main/java/info/nightscout/androidaps/Config.java b/app/src/main/java/info/nightscout/androidaps/Config.java index 7f094281f8..0707cb5f48 100644 --- a/app/src/main/java/info/nightscout/androidaps/Config.java +++ b/app/src/main/java/info/nightscout/androidaps/Config.java @@ -4,6 +4,8 @@ package info.nightscout.androidaps; * Created by mike on 07.06.2016. */ public class Config { + public static final boolean CACHECALCULATIONS = true; + // MAIN FUCTIONALITY public static final boolean APS = BuildConfig.APS; // PLUGINS 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 efb5f85fd0..9ecacf9e22 100644 --- a/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java +++ b/app/src/main/java/info/nightscout/androidaps/interfaces/TreatmentsInterface.java @@ -16,4 +16,5 @@ public interface TreatmentsInterface { IobTotal getCalculationToTime(long time); MealData getMealData(); List getTreatments(); + List getTreatments5MinBack(long time); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java new file mode 100644 index 0000000000..75ae83c440 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/AutosensData.java @@ -0,0 +1,20 @@ +package info.nightscout.androidaps.plugins.IobCobCalculator; + +import java.util.Date; + +/** + * Created by mike on 25.04.2017. + */ + +public class AutosensData { + String pastSensitivity = ""; + double deviation = 0d; + double absorbed = 0d; + double carbsFromBolus = 0d; + public double cob = 0; + + public String log(long time) { + return "AutosensData: " + new Date(time).toLocaleString() + " " + pastSensitivity + " Deviation=" + deviation + " Absorbed=" + absorbed + " CarbsFromBolus=" + carbsFromBolus + " COB=" + cob; + } + +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java index 198da5fdab..568c9b54eb 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobCalculatorPlugin.java @@ -3,6 +3,7 @@ package info.nightscout.androidaps.plugins.IobCobCalculator; import android.os.Handler; import android.os.HandlerThread; import android.support.annotation.Nullable; +import android.support.v4.util.LongSparseArray; import com.squareup.otto.Subscribe; @@ -15,16 +16,23 @@ import java.util.Date; import java.util.HashMap; import java.util.List; +import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.MealData; import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.db.Treatment; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.interfaces.PluginBase; +import info.nightscout.androidaps.interfaces.TreatmentsInterface; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; +import info.nightscout.androidaps.plugins.OpenAPSAMA.Autosens; +import info.nightscout.androidaps.plugins.Treatments.TreatmentsPlugin; +import info.nightscout.utils.SP; /** * Created by mike on 24.04.2017. @@ -33,8 +41,8 @@ import info.nightscout.androidaps.plugins.NSClientInternal.data.NSProfile; public class IobCobCalculatorPlugin implements PluginBase { private static Logger log = LoggerFactory.getLogger(IobCobCalculatorPlugin.class); - private static HashMap iobTable = new HashMap(); - private static HashMap mealDataTable = new HashMap(); + private static LongSparseArray iobTable = new LongSparseArray<>(); + private static LongSparseArray autosensDataTable = new LongSparseArray<>(); private static List bgReadings = null; // newest at index 0 private static List bucketed_data = null; @@ -121,6 +129,13 @@ public class IobCobCalculatorPlugin implements PluginBase { return -1; } + public static long roundUpTime(long time) { + if (time % 60000 == 0) + return time; + long rouded = (time / 60000 + 1) * 60000; + return rouded; + } + private void loadBgData() { onNewProfile(new EventNewBasalProfile(null)); bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime((long) (new Date().getTime() - 60 * 60 * 1000L * (24 + dia)), false); @@ -189,23 +204,139 @@ public class IobCobCalculatorPlugin implements PluginBase { log.debug("Bucketed data created. Size: " + bucketed_data.size()); } - public static IobTotal calulateFromTreatmentsAndTemps() { - ConfigBuilderPlugin.getActiveTreatments().updateTotalIOB(); - IobTotal bolusIob = ConfigBuilderPlugin.getActiveTreatments().getLastCalculation().round(); - ConfigBuilderPlugin.getActiveTempBasals().updateTotalIOB(); - IobTotal basalIob = ConfigBuilderPlugin.getActiveTempBasals().getLastCalculation().round(); - IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); - return iobTotal; + public void calculateSensitivityData() { + NSProfile profile = ConfigBuilderPlugin.getActiveProfile() != null ? ConfigBuilderPlugin.getActiveProfile().getProfile() : null; + + if (profile == null) { + log.debug("calculateSensitivityData: No profile available"); + return; + } + + if (ConfigBuilderPlugin.getActiveTreatments() == null) { + log.debug("calculateSensitivityData: No treatments plugin"); + return; + } + + TreatmentsInterface treatmentsInterface = ConfigBuilderPlugin.getActiveTreatments(); + if (bucketed_data == null || bucketed_data.size() < 3) { + log.debug("calculateSensitivityData: No bucketed data available"); + return; + } + + long prevDataTime = roundUpTime(bucketed_data.get(bucketed_data.size() - 3).timeIndex); + log.debug("Prev data time: " + new Date(prevDataTime).toLocaleString()); + AutosensData previous = autosensDataTable.get(prevDataTime); + // start from oldest to be able sub cob + for (int i = bucketed_data.size() - 4; i >= 0; i--) { + // check if data already exists + long bgTime = bucketed_data.get(i).timeIndex; + bgTime = roundUpTime(bgTime); + + AutosensData existing; + if ((existing = autosensDataTable.get(bgTime)) != null) { + previous = existing; + continue; + } + + int secondsFromMidnight = NSProfile.secondsFromMidnight(bgTime); + double sens = NSProfile.toMgdl(profile.getIsf(secondsFromMidnight), profile.getUnits()); + + AutosensData autosensData = new AutosensData(); + + //console.error(bgTime , bucketed_data[i].glucose); + double bg; + double avgDelta; + double delta; + bg = bucketed_data.get(i).value; + if (bg < 39 || bucketed_data.get(i + 3).value < 39) { + log.error("! value < 39"); + continue; + } + avgDelta = (bg - bucketed_data.get(i + 3).value) / 3; + delta = (bg - bucketed_data.get(i + 1).value); + + IobTotal iob = calulateFromTreatmentsAndTemps(bgTime); + + double bgi = Math.round((-iob.activity * sens * 5) * 100) / 100d; + double deviation = delta - bgi; + + List recentTreatments = treatmentsInterface.getTreatments5MinBack(bgTime); + for (int ir = 0; ir < recentTreatments.size(); ir++) { + autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; + } + + // if we absorbing carbs + if (previous != null && previous.cob > 0) { + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption + double ci = Math.max(deviation, SP.getDouble("openapsama_min_5m_carbimpact", 3.0)); + autosensData.absorbed = ci * profile.getIc(secondsFromMidnight) / sens; + // and add that to the running total carbsAbsorbed + autosensData.cob = Math.max(previous.cob - autosensData.absorbed, 0d); + } + autosensData.cob += autosensData.carbsFromBolus; + + // calculate autosens only without COB + if (autosensData.cob <= 0) { + if (deviation > 0) { + autosensData.pastSensitivity += "+"; + } else if (deviation == 0) { + autosensData.pastSensitivity += "="; + } else { + autosensData.pastSensitivity += "-"; + } + //avgDeltas[i] = avgDelta; + //bgis[i] = bgi; + autosensData.deviation = deviation; + } else { + autosensData.pastSensitivity += "C"; + //console.error(bgTime); + } + //log.debug("TIME: " + new Date(bgTime).toString() + " BG: " + bg + " SENS: " + sens + " DELTA: " + delta + " AVGDELTA: " + avgDelta + " IOB: " + iob.iob + " ACTIVITY: " + iob.activity + " BGI: " + bgi + " DEVIATION: " + deviation); + + previous = autosensData; + autosensDataTable.put(bgTime, autosensData); + log.debug(autosensData.log(bgTime)); + } + } public static IobTotal calulateFromTreatmentsAndTemps(long time) { + long now = new Date().getTime(); + time = roundUpTime(time); + if (Config.CACHECALCULATIONS && time < now && iobTable.get(time) != null) { + //log.debug(">>> Cache hit"); + return iobTable.get(time); + } else { + //log.debug(">>> Cache miss " + new Date(time).toLocaleString()); + } IobTotal bolusIob = ConfigBuilderPlugin.getActiveTreatments().getCalculationToTime(time).round(); IobTotal basalIob = ConfigBuilderPlugin.getActiveTempBasals().getCalculationToTime(time).round(); +/* if (basalIob.basaliob > 0) { log.debug(new Date(time).toLocaleString() + " basaliob: " + basalIob.basaliob ); } +*/ IobTotal iobTotal = IobTotal.combine(bolusIob, basalIob).round(); - return iobTotal; + if (Config.CACHECALCULATIONS && time < new Date().getTime()) { + iobTable.put(time, iobTotal); + } + return iobTotal; + } + + public static AutosensData getAutosensData(long time) { + long now = new Date().getTime(); + if (time > now ) + return null; + time = roundUpTime(time); + AutosensData data = autosensDataTable.get(time); + if (Config.CACHECALCULATIONS && data != null) { + log.debug(">>> Cache hit " + data.log(time)); + return data; + } else { + log.debug(">>> Cache miss " + new Date(time).toLocaleString()); + return null; + } } public static IobTotal[] calculateIobArrayInDia() { @@ -215,7 +346,7 @@ public class IobCobCalculatorPlugin implements PluginBase { int len = (int) ((profile.getDia() * 60 + 30) / 5); IobTotal[] array = new IobTotal[len]; int pos = 0; - for (int i = 0; i < len; i++){ + for (int i = 0; i < len; i++) { long t = time + i * 5 * 60000; IobTotal iob = calulateFromTreatmentsAndTemps(t); array[pos] = iob; @@ -226,7 +357,7 @@ public class IobCobCalculatorPlugin implements PluginBase { public static JSONArray convertToJSONArray(IobTotal[] iobArray) { JSONArray array = new JSONArray(); - for (int i = 0; i < iobArray.length; i ++) { + for (int i = 0; i < iobArray.length; i++) { array.put(iobArray[i].determineBasalJson()); } return array; @@ -239,6 +370,7 @@ public class IobCobCalculatorPlugin implements PluginBase { public void run() { loadBgData(); createBucketedData(); + calculateSensitivityData(); } }); } @@ -253,4 +385,27 @@ public class IobCobCalculatorPlugin implements PluginBase { } } + // When historical data is changed (comming from NS etc) finished calculations after this date must be invalidated + @Subscribe + public void onNewHistoryData(EventNewHistoryData ev) { + long time = ev.time; + log.debug("Invalidating cached data to: " + new Date(time).toLocaleString()); + for (int index = iobTable.size() - 1; index >= 0; index--) { + if (iobTable.keyAt(index) > time) { + log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString()); + iobTable.removeAt(index); + } else { + break; + } + } + for (int index = autosensDataTable.size() - 1; index >= 0; index--) { + if (autosensDataTable.keyAt(index) > time) { + log.debug("Removing from autosensDataTable: " + new Date(autosensDataTable.keyAt(index)).toLocaleString()); + autosensDataTable.removeAt(index); + } else { + break; + } + } + } + } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/events/EventNewHistoryData.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/events/EventNewHistoryData.java new file mode 100644 index 0000000000..7499fad383 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/events/EventNewHistoryData.java @@ -0,0 +1,13 @@ +package info.nightscout.androidaps.plugins.IobCobCalculator.events; + +/** + * Created by mike on 26.04.2017. + */ + +public class EventNewHistoryData { + public long time = 0; + + public EventNewHistoryData(long time) { + this.time = time; + } +} diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/Autosens.java b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/Autosens.java index 2629b1a11b..316dc37259 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/Autosens.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/OpenAPSAMA/Autosens.java @@ -25,7 +25,6 @@ public class Autosens { //console.error(mealTime); - double deviationSum = 0; double carbsAbsorbed = 0; List bucketed_data = IobCobCalculatorPlugin.getBucketedData(dataFromTime); @@ -33,8 +32,8 @@ public class Autosens { return new AutosensResult(); //console.error(bucketed_data); - double[] avgDeltas = new double[bucketed_data.size() - 2]; - double[] bgis = new double[bucketed_data.size() - 2]; + //double[] avgDeltas = new double[bucketed_data.size() - 2]; + //double[] bgis = new double[bucketed_data.size() - 2]; double[] deviations = new double[bucketed_data.size() - 2]; String pastSensitivity = ""; @@ -81,10 +80,9 @@ public class Autosens { } else { pastSensitivity += "-"; } - avgDeltas[i] = avgDelta; - bgis[i] = bgi; + //avgDeltas[i] = avgDelta; + //bgis[i] = bgi; deviations[i] = deviation; - deviationSum += deviation; } else { pastSensitivity += ">"; //console.error(bgTime); @@ -112,8 +110,8 @@ public class Autosens { log.debug(pastSensitivity); //console.log(JSON.stringify(avgDeltas)); //console.log(JSON.stringify(bgis)); - Arrays.sort(avgDeltas); - Arrays.sort(bgis); + //Arrays.sort(avgDeltas); + //Arrays.sort(bgis); Arrays.sort(deviations); for (double i = 0.9; i > 0.1; i = i - 0.02) { diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java index 596bd2a3de..629cfb9368 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Overview/OverviewFragment.java @@ -50,6 +50,10 @@ import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.Constants; @@ -76,6 +80,7 @@ import info.nightscout.androidaps.plugins.Careportal.Dialogs.NewNSTreatmentDialo import info.nightscout.androidaps.plugins.Careportal.OptionsToShow; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.IobCobCalculator.AutosensData; import info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin; import info.nightscout.androidaps.plugins.Loop.LoopPlugin; import info.nightscout.androidaps.plugins.Loop.events.EventNewOpenLoopNotification; @@ -155,6 +160,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, private static Handler sHandler; private static HandlerThread sHandlerThread; + private static final ScheduledExecutorService worker = Executors.newSingleThreadScheduledExecutor(); + private static ScheduledFuture scheduledUpdate = null; + public OverviewFragment() { super(); if (sHandlerThread == null) { @@ -272,19 +280,19 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, switch (buttonView.getId()) { case R.id.overview_showprediction: SP.putBoolean("showprediction", showPredictionView.isChecked()); - updateGUI("onPredictionCheckedChanged"); + scheduleUpdateGUI("onPredictionCheckedChanged"); break; case R.id.overview_showbasals: SP.putBoolean("showbasals", showPredictionView.isChecked()); - updateGUI("onBasalsCheckedChanged"); + scheduleUpdateGUI("onBasalsCheckedChanged"); break; case R.id.overview_showiob: SP.putBoolean("showiob", showIobView.isChecked()); - updateGUI("onIobCheckedChanged"); + scheduleUpdateGUI("onIobCheckedChanged"); break; case R.id.overview_showcob: SP.putBoolean("showcob", showCobView.isChecked()); - updateGUI("onCobCheckedChanged"); + scheduleUpdateGUI("onCobCheckedChanged"); break; } } @@ -296,15 +304,17 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, activeloop.setFragmentEnabled(PluginBase.LOOP, false); activeloop.setFragmentVisible(PluginBase.LOOP, false); MainApp.getConfigBuilder().storeSettings(); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.enableloop))) { activeloop.setFragmentEnabled(PluginBase.LOOP, true); activeloop.setFragmentVisible(PluginBase.LOOP, true); MainApp.getConfigBuilder().storeSettings(); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.resume))) { activeloop.suspendTo(0L); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); sHandler.post(new Runnable() { @Override public void run() { @@ -317,23 +327,23 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.suspendloopfor1h))) { activeloop.suspendTo(new Date().getTime() + 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.suspendloopfor2h))) { activeloop.suspendTo(new Date().getTime() + 2 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.suspendloopfor3h))) { activeloop.suspendTo(new Date().getTime() + 3 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.suspendloopfor10h))) { activeloop.suspendTo(new Date().getTime() + 10 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.disconnectpumpfor30m))) { activeloop.suspendTo(new Date().getTime() + 30L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); sHandler.post(new Runnable() { @Override public void run() { @@ -346,7 +356,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.disconnectpumpfor1h))) { activeloop.suspendTo(new Date().getTime() + 1 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); sHandler.post(new Runnable() { @Override public void run() { @@ -359,7 +369,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.disconnectpumpfor2h))) { activeloop.suspendTo(new Date().getTime() + 2 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); sHandler.post(new Runnable() { @Override public void run() { @@ -372,7 +382,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, return true; } else if (item.getTitle().equals(MainApp.sResources.getString(R.string.disconnectpumpfor3h))) { activeloop.suspendTo(new Date().getTime() + 3 * 60L * 60 * 1000); - updateGUI("suspendmenu"); + scheduleUpdateGUI("suspendmenu"); sHandler.post(new Runnable() { @Override public void run() { @@ -461,7 +471,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, objectivesPlugin.saveProgress(); } } - updateGUIIfVisible("onClickAcceptTemp"); + scheduleUpdateGUI("onClickAcceptTemp"); } }); Answers.getInstance().logCustom(new CustomEvent("AcceptTemp")); @@ -585,58 +595,58 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, sRefreshLoop = new Runnable() { @Override public void run() { - updateGUIIfVisible("refreshLoop"); + scheduleUpdateGUI("refreshLoop"); sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); } }; sLoopHandler.postDelayed(sRefreshLoop, 60 * 1000L); registerForContextMenu(apsModeView); - updateGUIIfVisible("onResume"); + updateGUI("onResume"); } @Subscribe public void onStatusEvent(final EventInitializationChanged ev) { - updateGUIIfVisible("EventInitializationChanged"); + scheduleUpdateGUI("EventInitializationChanged"); } @Subscribe public void onStatusEvent(final EventPreferenceChange ev) { - updateGUIIfVisible("EventPreferenceChange"); + scheduleUpdateGUI("EventPreferenceChange"); } @Subscribe public void onStatusEvent(final EventRefreshGui ev) { - updateGUIIfVisible("EventRefreshGui"); + scheduleUpdateGUI("EventRefreshGui"); } @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { - updateGUIIfVisible("EventTreatmentChange"); + scheduleUpdateGUI("EventTreatmentChange"); } @Subscribe public void onStatusEvent(final EventTempBasalChange ev) { - updateGUIIfVisible("EventTempBasalChange"); + scheduleUpdateGUI("EventTempBasalChange"); } @Subscribe public void onStatusEvent(final EventNewBG ev) { - updateGUIIfVisible("EventTempBasalChange"); + scheduleUpdateGUI("EventTempBasalChange"); } @Subscribe public void onStatusEvent(final EventNewOpenLoopNotification ev) { - updateGUIIfVisible("EventNewOpenLoopNotification"); + scheduleUpdateGUI("EventNewOpenLoopNotification"); } @Subscribe public void onStatusEvent(final EventNewBasalProfile ev) { - updateGUIIfVisible("EventNewBasalProfile"); + scheduleUpdateGUI("EventNewBasalProfile"); } @Subscribe public void onStatusEvent(final EventTempTargetRangeChange ev) { - updateGUIIfVisible("EventTempTargetRangeChange"); + scheduleUpdateGUI("EventTempTargetRangeChange"); } @Subscribe @@ -672,17 +682,6 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, }); } - private void updateGUIIfVisible(final String from) { - Activity activity = getActivity(); - if (activity != null) - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - updateGUI(from); - } - }); - } - private void updatePumpStatus(String status) { PumpInterface pump = MainApp.getConfigBuilder(); if (!status.equals("")) { @@ -697,6 +696,29 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, } } + public void scheduleUpdateGUI(final String from) { + class UpdateRunnable implements Runnable { + public void run() { + Activity activity = getActivity(); + if (activity != null) + activity.runOnUiThread(new Runnable() { + @Override + public void run() { + updateGUI(from); + scheduledUpdate = null; + } + }); + } + } + // prepare task for execution in 400 msec + // cancel waiting task to prevent multiple updates + if (scheduledUpdate != null) + scheduledUpdate.cancel(false); + Runnable task = new UpdateRunnable(); + final int msec = 400; + scheduledUpdate = worker.schedule(task, msec, TimeUnit.MILLISECONDS); + } + @SuppressLint("SetTextI18n") public void updateGUI(String from) { log.debug("updateGUI entered from: " + from); @@ -1044,7 +1066,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(2); paint.setPathEffect(new DashPathEffect(new float[]{2, 4}, 0)); - paint.setColor(Color.CYAN); + paint.setColor(MainApp.sResources.getColor(R.color.basal)); basalsLineSeries.setCustomPaint(paint); } @@ -1058,48 +1080,56 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, Date start = new Date(); List iobArray = new ArrayList<>(); List cobArray = new ArrayList<>(); - double lastIob = -1000; - for (long time = fromTime; time <= endTime; time += 5 * 60 * 1000L) { + for (long time = fromTime; time <= now; time += 5 * 60 * 1000L) { if (showIobView.isChecked()) { IobTotal iob = IobCobCalculatorPlugin.calulateFromTreatmentsAndTemps(time); - if (lastIob != iob.iob) { - iobArray.add(new DataPoint(time, iob.iob)); - maxIobValueFound = Math.max(maxIobValueFound, Math.abs(iob.iob)); - lastIob = iob.iob; - } + iobArray.add(new DataPoint(time, iob.iob)); + maxIobValueFound = Math.max(maxIobValueFound, Math.abs(iob.iob)); } if (showCobView.isChecked()) { - //MealData mealData = MainApp.getConfigBuilder().getActiveTreatments().getMealData(); - //cobArray.add(new DataPoint(time, mealData.mealCOB)); - //maxCobValueFound = Math.max(maxCobValueFound, mealData.mealCOB); + AutosensData autosensData = IobCobCalculatorPlugin.getAutosensData(time); + if (autosensData != null) { + cobArray.add(new DataPoint(time, autosensData.cob)); + maxCobValueFound = Math.max(maxCobValueFound, autosensData.cob); + } } } - Profiler.log(log,"IOB precessed", start); + Profiler.log(log, "IOB processed", start); DataPoint[] iobData = new DataPoint[iobArray.size()]; iobData = iobArray.toArray(iobData); iobSeries = new FixedLineGraphSeries<>(iobData); iobSeries.setDrawBackground(true); - iobSeries.setBackgroundColor(0x80FFFFFF & MainApp.sResources.getColor(R.color.ioborange)); //50% - iobSeries.setColor(MainApp.sResources.getColor(R.color.ioborange)); + iobSeries.setBackgroundColor(0x80FFFFFF & MainApp.sResources.getColor(R.color.iob)); //50% + iobSeries.setColor(MainApp.sResources.getColor(R.color.iob)); iobSeries.setThickness(3); iobSeries.setTitle("IOB"); iobGraph.getGridLabelRenderer().setVerticalLabelsAlign(Paint.Align.LEFT); + + if (showIobView.isChecked() && showCobView.isChecked()) { + List cobArrayRescaled = new ArrayList<>(); + for (int ci = 0; ci < cobArray.size(); ci++) { + cobArrayRescaled.add(new DataPoint(cobArray.get(ci).getX(), cobArray.get(ci).getY() * maxIobValueFound / maxCobValueFound / 2)); + } + cobArray = cobArrayRescaled; + } DataPoint[] cobData = new DataPoint[cobArray.size()]; cobData = cobArray.toArray(cobData); cobSeries = new FixedLineGraphSeries<>(cobData); cobSeries.setDrawBackground(true); - cobSeries.setBackgroundColor(Color.RED); - cobSeries.setThickness(0); + cobSeries.setBackgroundColor(0xB0FFFFFF & MainApp.sResources.getColor(R.color.cob)); //50% + cobSeries.setColor(MainApp.sResources.getColor(R.color.cob)); + cobSeries.setThickness(3); + cobSeries.setTitle("COB"); iobGraph.removeAllSeries(); if (showIobView.isChecked()) { iobGraph.addSeries(iobSeries); } - if (showCobView.isChecked()) { - iobGraph.getSecondScale().addSeries(cobSeries); - iobGraph.getSecondScale().setLabelFormatter(new LabelFormatter() { + if (showCobView.isChecked() && cobData.length > 0) { + iobGraph.addSeries(cobSeries); + /* iobGraph.getSecondScale().setLabelFormatter(new LabelFormatter() { @Override public String formatLabel(double value, boolean isValueX) { return ""; @@ -1107,10 +1137,9 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, @Override public void setViewport(Viewport viewport) { - } }); - } +*/ } iobGraphLayout.setVisibility(View.VISIBLE); } else { iobGraphLayout.setVisibility(View.GONE); @@ -1194,7 +1223,7 @@ public class OverviewFragment extends Fragment implements View.OnClickListener, bgGraph.addSeries(predSeries = new PointsGraphSeries(pred)); predSeries.setShape(PointsGraphSeries.Shape.POINT); predSeries.setSize(4); - predSeries.setColor(Color.MAGENTA); + predSeries.setColor(MainApp.sResources.getColor(R.color.prediction)); } } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java index 16e333719b..90ed870574 100644 --- a/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java +++ b/app/src/main/java/info/nightscout/androidaps/plugins/Treatments/TreatmentsPlugin.java @@ -5,6 +5,7 @@ import com.squareup.otto.Subscribe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -163,6 +164,17 @@ public class TreatmentsPlugin implements PluginBase, TreatmentsInterface { return treatments; } + @Override + public List getTreatments5MinBack(long time) { + List in5minback = new ArrayList<>(); + for (Integer pos = 0; pos < treatments.size(); pos++) { + Treatment t = treatments.get(pos); + if (t.created_at.getTime() <= time && t.created_at.getTime() > time - 5 * 60 * 1000) + in5minback.add(t); + } + return in5minback; + } + @Subscribe public void onStatusEvent(final EventTreatmentChange ev) { initializeData(); diff --git a/app/src/main/res/layout/overview_fragment.xml b/app/src/main/res/layout/overview_fragment.xml index 7b933aeb4b..041a248bc0 100644 --- a/app/src/main/res/layout/overview_fragment.xml +++ b/app/src/main/res/layout/overview_fragment.xml @@ -218,16 +218,6 @@ android:layout_width="wrap_content" android:layout_height="80dip" /> - @@ -237,30 +227,73 @@ android:layout_height="match_parent" android:orientation="vertical"> + + + android:layout_marginBottom="-5dp" + android:layout_marginTop="-9dp" + app:buttonTint="@color/prediction" /> + + + android:layout_marginBottom="-5dp" + android:layout_marginTop="-9dp" + app:buttonTint="@color/basal" /> + + + android:layout_marginBottom="-5dp" + android:layout_marginTop="-9dp" + app:buttonTint="@color/iob" /> + + + android:layout_marginBottom="-5dp" + android:layout_marginTop="-9dp" + app:buttonTint="@color/cob" /> diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c857af7b9b..3c933a1ce9 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,8 +1,9 @@ - #ff00ff - #00ffff - #FFFB8C00 + #ff00ff + #00ffff + #FFFB8C00 + #fbf300 #3F51B5 #303F9F diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 139243af3d..69a6a52c74 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -593,4 +593,7 @@ Enable superbolus in wizard Enable superbolus functionality in wizard. Do not enable until you learn what it really does. IT MAY CAUSE INSULIN OVERDOSE IF USED BLINDLY! IOB + COB + PRE + BAS