diff --git a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java index e4d65eadc0..b242c4c099 100644 --- a/app/src/main/java/info/nightscout/androidaps/Services/DataService.java +++ b/app/src/main/java/info/nightscout/androidaps/Services/DataService.java @@ -15,20 +15,22 @@ import org.slf4j.LoggerFactory; import info.nightscout.androidaps.Config; import info.nightscout.androidaps.MainApp; import info.nightscout.androidaps.R; +import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.CareportalEvent; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; import info.nightscout.androidaps.plugins.ConstraintsObjectives.ObjectivesPlugin; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSMbg; -import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv; -import info.nightscout.androidaps.data.ProfileStore; import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSettingsStatus; -import info.nightscout.androidaps.plugins.Overview.notifications.Notification; +import info.nightscout.androidaps.plugins.NSClientInternal.data.NSSgv; import info.nightscout.androidaps.plugins.Overview.OverviewPlugin; import info.nightscout.androidaps.plugins.Overview.events.EventDismissNotification; import info.nightscout.androidaps.plugins.Overview.events.EventNewNotification; +import info.nightscout.androidaps.plugins.Overview.notifications.Notification; import info.nightscout.androidaps.plugins.ProfileNS.NSProfilePlugin; +import info.nightscout.androidaps.plugins.ProfileNS.events.EventNSProfileUpdateGUI; import info.nightscout.androidaps.plugins.PumpDanaR.activities.DanaRNSHistorySync; import info.nightscout.androidaps.plugins.SmsCommunicator.events.EventNewSMS; import info.nightscout.androidaps.plugins.SourceDexcomG5.SourceDexcomG5Plugin; @@ -37,7 +39,6 @@ import info.nightscout.androidaps.plugins.SourceMM640g.SourceMM640gPlugin; import info.nightscout.androidaps.plugins.SourceNSClient.SourceNSClientPlugin; import info.nightscout.androidaps.plugins.SourceXdrip.SourceXdripPlugin; import info.nightscout.androidaps.receivers.DataReceiver; -import info.nightscout.androidaps.plugins.NSClientInternal.data.NSDeviceStatus; import info.nightscout.utils.BundleLogger; import info.nightscout.utils.NSUpload; import info.nightscout.utils.SP; @@ -365,8 +366,10 @@ public class DataService extends IntentService { String profile = bundles.getString("profile"); ProfileStore profileStore = new ProfileStore(new JSONObject(profile)); NSProfilePlugin.storeNewProfile(profileStore); - MainApp.bus().post(new EventNewBasalProfile()); - + MainApp.bus().post(new EventNSProfileUpdateGUI()); + // if there are no profile switches this should lead to profile update + if (MainApp.getConfigBuilder().getProfileSwitchesFromHistory().size() == 0) + MainApp.bus().post(new EventNewBasalProfile()); if (Config.logIncommingData) log.debug("Received profileStore: " + activeProfile + " " + profile); } catch (JSONException e) { diff --git a/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java b/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java new file mode 100644 index 0000000000..17262cfb85 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/events/EventAppInitialized.java @@ -0,0 +1,8 @@ +package info.nightscout.androidaps.events; + +/** + * Created by mike on 23.01.2018. + */ + +public class EventAppInitialized extends Event { +} 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 99ff546eac..e63924d80a 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 @@ -28,6 +28,7 @@ import info.nightscout.androidaps.db.ProfileSwitch; import info.nightscout.androidaps.db.TempTarget; import info.nightscout.androidaps.db.TemporaryBasal; import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.events.EventAppInitialized; import info.nightscout.androidaps.interfaces.APSInterface; import info.nightscout.androidaps.interfaces.BgSourceInterface; import info.nightscout.androidaps.interfaces.ConstraintsInterface; @@ -144,6 +145,7 @@ public class ConfigBuilderPlugin implements PluginBase, ConstraintsInterface, Tr public void initialize() { pluginList = MainApp.getPluginsList(); loadSettings(); + MainApp.bus().post(new EventAppInitialized()); } public void storeSettings() { 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 3522d3fc1e..fe09891dd7 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 @@ -1,8 +1,6 @@ package info.nightscout.androidaps.plugins.IobCobCalculator; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; +import android.os.SystemClock; import android.support.annotation.Nullable; import android.support.v4.util.LongSparseArray; @@ -24,14 +22,13 @@ import info.nightscout.androidaps.data.IobTotal; import info.nightscout.androidaps.data.Profile; import info.nightscout.androidaps.db.BgReading; import info.nightscout.androidaps.db.TemporaryBasal; -import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.events.EventAppInitialized; import info.nightscout.androidaps.events.EventConfigBuilderChange; import info.nightscout.androidaps.events.EventNewBG; import info.nightscout.androidaps.events.EventNewBasalProfile; import info.nightscout.androidaps.events.EventPreferenceChange; import info.nightscout.androidaps.interfaces.PluginBase; import info.nightscout.androidaps.plugins.ConfigBuilder.ConfigBuilderPlugin; -import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventNewHistoryData; import info.nightscout.utils.DateUtil; @@ -51,10 +48,10 @@ public class IobCobCalculatorPlugin implements PluginBase { private static double dia = Constants.defaultDIA; - private static Handler sHandler = null; - private static HandlerThread sHandlerThread = null; + static final Object dataLock = new Object(); - private static final Object dataLock = new Object(); + boolean stopCalculationTrigger = false; + IobCobThread thread = null; private static IobCobCalculatorPlugin plugin = null; @@ -134,12 +131,6 @@ public class IobCobCalculatorPlugin implements PluginBase { IobCobCalculatorPlugin() { MainApp.bus().register(this); - if (sHandlerThread == null) { - sHandlerThread = new HandlerThread(IobCobCalculatorPlugin.class.getSimpleName(), Process.THREAD_PRIORITY_LOWEST); - sHandlerThread.start(); - sHandler = new Handler(sHandlerThread.getLooper()); - } - onNewBg(new EventNewBG()); } @Nullable @@ -176,14 +167,9 @@ public class IobCobCalculatorPlugin implements PluginBase { return rouded; } - private void loadBgData() { - //log.debug("Locking loadBgData"); - synchronized (dataLock) { - onNewProfile(null); - bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime((long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)), false); - log.debug("BG data loaded. Size: " + bgReadings.size()); - } - //log.debug("Releasing loadBgData"); + void loadBgData() { + bgReadings = MainApp.getDbHelper().getBgreadingsDataFromTime((long) (System.currentTimeMillis() - 60 * 60 * 1000L * (24 + dia)), false); + log.debug("BG data loaded. Size: " + bgReadings.size()); } private boolean isAbout5minData() { @@ -210,7 +196,7 @@ public class IobCobCalculatorPlugin implements PluginBase { } } - private void createBucketedData() { + void createBucketedData() { if (isAbout5minData()) createBucketedData5min(); else @@ -242,223 +228,97 @@ public class IobCobCalculatorPlugin implements PluginBase { } private void createBucketedDataRecalculated() { - synchronized (dataLock) { - if (bgReadings == null || bgReadings.size() < 3) { - bucketed_data = null; - return; - } + if (bgReadings == null || bgReadings.size() < 3) { + bucketed_data = null; + return; + } - bucketed_data = new ArrayList<>(); - long currentTime = bgReadings.get(0).date + 5 * 60 * 1000 - bgReadings.get(0).date % (5 * 60 * 1000) - 5 * 60 * 1000L; - //log.debug("First reading: " + new Date(currentTime).toLocaleString()); + bucketed_data = new ArrayList<>(); + long currentTime = bgReadings.get(0).date + 5 * 60 * 1000 - bgReadings.get(0).date % (5 * 60 * 1000) - 5 * 60 * 1000L; + //log.debug("First reading: " + new Date(currentTime).toLocaleString()); - while (true) { - // test if current value is older than current time - BgReading newer = findNewer(currentTime); - BgReading older = findOlder(currentTime); - if (newer == null || older == null) - break; + while (true) { + // test if current value is older than current time + BgReading newer = findNewer(currentTime); + BgReading older = findOlder(currentTime); + if (newer == null || older == null) + break; - double bgDelta = newer.value - older.value; - long timeDiffToNew = newer.date - currentTime; + double bgDelta = newer.value - older.value; + long timeDiffToNew = newer.date - currentTime; - double currentBg = newer.value - (double) timeDiffToNew / (newer.date - older.date) * bgDelta; - BgReading newBgreading = new BgReading(); - newBgreading.date = currentTime; - newBgreading.value = Math.round(currentBg); - bucketed_data.add(newBgreading); - //log.debug("BG: " + newBgreading.value + " (" + new Date(newBgreading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")"); - currentTime -= 5 * 60 * 1000L; + double currentBg = newer.value - (double) timeDiffToNew / (newer.date - older.date) * bgDelta; + BgReading newBgreading = new BgReading(); + newBgreading.date = currentTime; + newBgreading.value = Math.round(currentBg); + bucketed_data.add(newBgreading); + //log.debug("BG: " + newBgreading.value + " (" + new Date(newBgreading.date).toLocaleString() + ") Prev: " + older.value + " (" + new Date(older.date).toLocaleString() + ") Newer: " + newer.value + " (" + new Date(newer.date).toLocaleString() + ")"); + currentTime -= 5 * 60 * 1000L; - } } } public void createBucketedData5min() { - //log.debug("Locking createBucketedData"); - synchronized (dataLock) { - if (bgReadings == null || bgReadings.size() < 3) { - bucketed_data = null; - return; + if (bgReadings == null || bgReadings.size() < 3) { + bucketed_data = null; + return; + } + + bucketed_data = new ArrayList<>(); + bucketed_data.add(bgReadings.get(0)); + int j = 0; + for (int i = 1; i < bgReadings.size(); ++i) { + long bgTime = bgReadings.get(i).date; + long lastbgTime = bgReadings.get(i - 1).date; + //log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastbgTime).toString() + " " + bgReadings.get(i - 1).value); + if (bgReadings.get(i).value < 39 || bgReadings.get(i - 1).value < 39) { + continue; } - bucketed_data = new ArrayList<>(); - bucketed_data.add(bgReadings.get(0)); - int j = 0; - for (int i = 1; i < bgReadings.size(); ++i) { - long bgTime = bgReadings.get(i).date; - long lastbgTime = bgReadings.get(i - 1).date; - //log.error("Processing " + i + ": " + new Date(bgTime).toString() + " " + bgReadings.get(i).value + " Previous: " + new Date(lastbgTime).toString() + " " + bgReadings.get(i - 1).value); - if (bgReadings.get(i).value < 39 || bgReadings.get(i - 1).value < 39) { - continue; - } - - long elapsed_minutes = (bgTime - lastbgTime) / (60 * 1000); - if (Math.abs(elapsed_minutes) > 8) { - // interpolate missing data points - double lastbg = bgReadings.get(i - 1).value; - elapsed_minutes = Math.abs(elapsed_minutes); - //console.error(elapsed_minutes); - long nextbgTime; - while (elapsed_minutes > 5) { - nextbgTime = lastbgTime - 5 * 60 * 1000; - j++; - BgReading newBgreading = new BgReading(); - newBgreading.date = nextbgTime; - double gapDelta = bgReadings.get(i).value - lastbg; - //console.error(gapDelta, lastbg, elapsed_minutes); - double nextbg = lastbg + (5d / elapsed_minutes * gapDelta); - newBgreading.value = Math.round(nextbg); - //console.error("Interpolated", bucketed_data[j]); - bucketed_data.add(newBgreading); - //log.error("******************************************************************************************************* Adding:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); - - elapsed_minutes = elapsed_minutes - 5; - lastbg = nextbg; - lastbgTime = nextbgTime; - } + long elapsed_minutes = (bgTime - lastbgTime) / (60 * 1000); + if (Math.abs(elapsed_minutes) > 8) { + // interpolate missing data points + double lastbg = bgReadings.get(i - 1).value; + elapsed_minutes = Math.abs(elapsed_minutes); + //console.error(elapsed_minutes); + long nextbgTime; + while (elapsed_minutes > 5) { + nextbgTime = lastbgTime - 5 * 60 * 1000; j++; BgReading newBgreading = new BgReading(); - newBgreading.value = bgReadings.get(i).value; - newBgreading.date = bgTime; + newBgreading.date = nextbgTime; + double gapDelta = bgReadings.get(i).value - lastbg; + //console.error(gapDelta, lastbg, elapsed_minutes); + double nextbg = lastbg + (5d / elapsed_minutes * gapDelta); + newBgreading.value = Math.round(nextbg); + //console.error("Interpolated", bucketed_data[j]); bucketed_data.add(newBgreading); - //log.error("******************************************************************************************************* Copying:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); - } else if (Math.abs(elapsed_minutes) > 2) { - j++; - BgReading newBgreading = new BgReading(); - newBgreading.value = bgReadings.get(i).value; - newBgreading.date = bgTime; - bucketed_data.add(newBgreading); - //log.error("******************************************************************************************************* Copying:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); - } else { - bucketed_data.get(j).value = (bucketed_data.get(j).value + bgReadings.get(i).value) / 2; - //log.error("***** Average"); + //log.error("******************************************************************************************************* Adding:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); + + elapsed_minutes = elapsed_minutes - 5; + lastbg = nextbg; + lastbgTime = nextbgTime; } - } - log.debug("Bucketed data created. Size: " + bucketed_data.size()); - } - //log.debug("Releasing createBucketedData"); - } - - private void calculateSensitivityData() { - if (MainApp.getConfigBuilder() == null) - return; // app still initializing - if (MainApp.getConfigBuilder().getProfile() == null) - return; // app still initializing - //log.debug("Locking calculateSensitivityData"); - long oldestTimeWithData = oldestDataAvailable(); - - synchronized (dataLock) { - - 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).date); - 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).date; - bgTime = roundUpTime(bgTime); - if (bgTime > System.currentTimeMillis()) - continue; - Profile profile = MainApp.getConfigBuilder().getProfile(bgTime); - - AutosensData existing; - if ((existing = autosensDataTable.get(bgTime)) != null) { - previous = existing; - continue; - } - - if (profile.getIsf(bgTime) == null) - return; // profile not set yet - - double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); - - AutosensData autosensData = new AutosensData(); - autosensData.time = bgTime; - if (previous != null) - autosensData.activeCarbsList = new ArrayList<>(previous.activeCarbsList); - else - autosensData.activeCarbsList = new ArrayList<>(); - - //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; - } - delta = (bg - bucketed_data.get(i + 1).value); - - IobTotal iob = calculateFromTreatmentsAndTemps(bgTime); - - double bgi = -iob.activity * sens * 5; - double deviation = delta - bgi; - - List recentTreatments = MainApp.getConfigBuilder().getTreatments5MinBackFromHistory(bgTime); - for (int ir = 0; ir < recentTreatments.size(); ir++) { - autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; - autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); - } - - - // if we are absorbing carbs - if (previous != null && previous.cob > 0) { - // calculate sum of min carb impact from all active treatments - double totalMinCarbsImpact = 0d; - for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) { - AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii); - totalMinCarbsImpact += c.min5minCarbImpact; - } - - // figure out how many carbs that represents - // but always assume at least 3mg/dL/5m (default) absorption per active treatment - double ci = Math.max(deviation, totalMinCarbsImpact); - autosensData.absorbed = ci * profile.getIc(bgTime) / sens; - // and add that to the running total carbsAbsorbed - autosensData.cob = Math.max(previous.cob - autosensData.absorbed, 0d); - autosensData.substractAbosorbedCarbs(); - } - autosensData.removeOldCarbs(bgTime); - autosensData.cob += autosensData.carbsFromBolus; - autosensData.deviation = deviation; - autosensData.bgi = bgi; - autosensData.delta = delta; - - // calculate autosens only without COB - if (autosensData.cob <= 0) { - if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { - autosensData.pastSensitivity += "="; - autosensData.nonEqualDeviation = true; - } else if (deviation > 0) { - autosensData.pastSensitivity += "+"; - autosensData.nonEqualDeviation = true; - } else { - autosensData.pastSensitivity += "-"; - autosensData.nonEqualDeviation = true; - } - autosensData.nonCarbsDeviation = true; - } else { - autosensData.pastSensitivity += "C"; - } - //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); - autosensData.autosensRatio = detectSensitivity(oldestTimeWithData, bgTime).ratio; - if (Config.logAutosensData) - log.debug(autosensData.log(bgTime)); + j++; + BgReading newBgreading = new BgReading(); + newBgreading.value = bgReadings.get(i).value; + newBgreading.date = bgTime; + bucketed_data.add(newBgreading); + //log.error("******************************************************************************************************* Copying:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); + } else if (Math.abs(elapsed_minutes) > 2) { + j++; + BgReading newBgreading = new BgReading(); + newBgreading.value = bgReadings.get(i).value; + newBgreading.date = bgTime; + bucketed_data.add(newBgreading); + //log.error("******************************************************************************************************* Copying:" + new Date(newBgreading.date).toString() + " " + newBgreading.value); + } else { + bucketed_data.get(j).value = (bucketed_data.get(j).value + bgReadings.get(i).value) / 2; + //log.error("***** Average"); } } - MainApp.bus().post(new EventAutosensCalculationFinished()); - //log.debug("Releasing calculateSensitivityData"); + log.debug("Bucketed data created. Size: " + bucketed_data.size()); } public static long oldestDataAvailable() { @@ -613,7 +473,7 @@ public class IobCobCalculatorPlugin implements PluginBase { } } - private static AutosensResult detectSensitivity(long fromTime, long toTime) { + static AutosensResult detectSensitivity(long fromTime, long toTime) { return ConfigBuilderPlugin.getActiveSensitivity().detectSensitivity(fromTime, toTime); } @@ -626,15 +486,33 @@ public class IobCobCalculatorPlugin implements PluginBase { } @Subscribe - public void onNewBg(EventNewBG ev) { - sHandler.post(new Runnable() { - @Override - public void run() { - loadBgData(); - createBucketedData(); - calculateSensitivityData(); + public void onEventAppInitialized(EventAppInitialized ev) { + runCalculation("onEventAppInitialized", true); + } + + @Subscribe + public void onEventNewBG(EventNewBG ev) { + stopCalculation("onEventNewBG"); + runCalculation("onEventNewBG", true); + } + + private void stopCalculation(String from) { + if (thread != null && thread.getState() != Thread.State.TERMINATED) { + stopCalculationTrigger = true; + log.debug("Stopping calculation thread: " + from); + while (thread.getState() != Thread.State.TERMINATED) { + SystemClock.sleep(100); } - }); + log.debug("Calculation thread stopped: " + from); + } + } + + private void runCalculation(String from, boolean bgDataReload) { + log.debug("Starting calculation thread: " + from); + if (thread == null || thread.getState() == Thread.State.TERMINATED) { + thread = new IobCobThread(this, from, bgDataReload); + thread.start(); + } } @Subscribe @@ -648,66 +526,54 @@ public class IobCobCalculatorPlugin implements PluginBase { if (ev == null) { // on init no need of reset return; } + stopCalculation("onNewProfile"); synchronized (dataLock) { log.debug("Invalidating cached data because of new profile. IOB: " + iobTable.size() + " Autosens: " + autosensDataTable.size() + " records"); iobTable = new LongSparseArray<>(); autosensDataTable = new LongSparseArray<>(); } - sHandler.post(new Runnable() { - @Override - public void run() { - calculateSensitivityData(); - } - }); + runCalculation("onNewProfile", false); } @Subscribe - public void onStatusEvent(EventPreferenceChange ev) { + public void onEventPreferenceChange(EventPreferenceChange ev) { if (ev.isChanged(R.string.key_openapsama_autosens_period) || ev.isChanged(R.string.key_age) || ev.isChanged(R.string.key_absorption_maxtime) ) { + stopCalculation("onEventPreferenceChange"); synchronized (dataLock) { log.debug("Invalidating cached data because of preference change. IOB: " + iobTable.size() + " Autosens: " + autosensDataTable.size() + " records"); iobTable = new LongSparseArray<>(); autosensDataTable = new LongSparseArray<>(); } - sHandler.post(new Runnable() { - @Override - public void run() { - calculateSensitivityData(); - } - }); + runCalculation("onEventPreferenceChange", false); } } @Subscribe - public void onStatusEvent(EventConfigBuilderChange ev) { + public void onEventConfigBuilderChange(EventConfigBuilderChange ev) { + stopCalculation("onEventConfigBuilderChange"); synchronized (dataLock) { log.debug("Invalidating cached data because of configuration change. IOB: " + iobTable.size() + " Autosens: " + autosensDataTable.size() + " records"); iobTable = new LongSparseArray<>(); autosensDataTable = new LongSparseArray<>(); } - sHandler.post(new Runnable() { - @Override - public void run() { - calculateSensitivityData(); - } - }); + runCalculation("onEventConfigBuilderChange", false); } // When historical data is changed (comming from NS etc) finished calculations after this date must be invalidated @Subscribe - public void onNewHistoryData(EventNewHistoryData ev) { + public void onEventNewHistoryData(EventNewHistoryData ev) { //log.debug("Locking onNewHistoryData"); + stopCalculation("onEventNewHistoryData"); synchronized (dataLock) { 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) { if (Config.logAutosensData) - if (Config.logAutosensData) - log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString()); + log.debug("Removing from iobTable: " + new Date(iobTable.keyAt(index)).toLocaleString()); iobTable.removeAt(index); } else { break; @@ -732,12 +598,7 @@ public class IobCobCalculatorPlugin implements PluginBase { } } } - sHandler.post(new Runnable() { - @Override - public void run() { - calculateSensitivityData(); - } - }); + runCalculation("onEventNewHistoryData", false); //log.debug("Releasing onNewHistoryData"); } diff --git a/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java new file mode 100644 index 0000000000..0dc3663ac6 --- /dev/null +++ b/app/src/main/java/info/nightscout/androidaps/plugins/IobCobCalculator/IobCobThread.java @@ -0,0 +1,201 @@ +package info.nightscout.androidaps.plugins.IobCobCalculator; + +import android.content.Context; +import android.os.PowerManager; +import android.support.v4.util.LongSparseArray; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +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.data.IobTotal; +import info.nightscout.androidaps.data.Profile; +import info.nightscout.androidaps.db.BgReading; +import info.nightscout.androidaps.db.Treatment; +import info.nightscout.androidaps.plugins.IobCobCalculator.events.EventAutosensCalculationFinished; +import info.nightscout.androidaps.queue.QueueThread; + +import static info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin.getBucketedData; +import static info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin.oldestDataAvailable; +import static info.nightscout.androidaps.plugins.IobCobCalculator.IobCobCalculatorPlugin.roundUpTime; + +/** + * Created by mike on 23.01.2018. + */ + +public class IobCobThread extends Thread { + private static Logger log = LoggerFactory.getLogger(QueueThread.class); + + private IobCobCalculatorPlugin iobCobCalculatorPlugin; + private boolean bgDataReload; + private String from; + + private PowerManager.WakeLock mWakeLock; + + public IobCobThread(IobCobCalculatorPlugin plugin, String from, boolean bgDataReload) { + super(); + + this.iobCobCalculatorPlugin = plugin; + this.bgDataReload = bgDataReload; + this.from = from; + + PowerManager powerManager = (PowerManager) MainApp.instance().getApplicationContext().getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "iobCobThread"); + } + + @Override + public final void run() { + mWakeLock.acquire(); + try { + if (MainApp.getConfigBuilder() == null) { + log.debug("Aborting calculation thread (ConfigBuilder not ready): " + from); + return; // app still initializing + } + if (MainApp.getConfigBuilder().getProfile() == null) { + log.debug("Aborting calculation thread (No profile): " + from); + return; // app still initializing + } + //log.debug("Locking calculateSensitivityData"); + + Object dataLock = iobCobCalculatorPlugin.dataLock; + + long oldestTimeWithData = oldestDataAvailable(); + + synchronized (dataLock) { + if (bgDataReload) { + iobCobCalculatorPlugin.loadBgData(); + iobCobCalculatorPlugin.createBucketedData(); + } + List bucketed_data = getBucketedData(); + LongSparseArray autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable(); + + if (bucketed_data == null || bucketed_data.size() < 3) { + log.debug("Aborting calculation thread (No bucketed data available): " + from); + return; + } + + long prevDataTime = roundUpTime(bucketed_data.get(bucketed_data.size() - 3).date); + 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--) { + if (iobCobCalculatorPlugin.stopCalculationTrigger) { + iobCobCalculatorPlugin.stopCalculationTrigger = false; + log.debug("Aborting calculation thread (trigger): " + from); + return; + } + // check if data already exists + long bgTime = bucketed_data.get(i).date; + bgTime = roundUpTime(bgTime); + if (bgTime > System.currentTimeMillis()) + continue; + Profile profile = MainApp.getConfigBuilder().getProfile(bgTime); + + AutosensData existing; + if ((existing = autosensDataTable.get(bgTime)) != null) { + previous = existing; + continue; + } + + if (profile.getIsf(bgTime) == null) { + log.debug("Aborting calculation thread (no ISF): " + from); + return; // profile not set yet + } + + if (Config.logAutosensData) + log.debug("Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")"); + + double sens = Profile.toMgdl(profile.getIsf(bgTime), profile.getUnits()); + + AutosensData autosensData = new AutosensData(); + autosensData.time = bgTime; + if (previous != null) + autosensData.activeCarbsList = new ArrayList<>(previous.activeCarbsList); + else + autosensData.activeCarbsList = new ArrayList<>(); + + //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; + } + delta = (bg - bucketed_data.get(i + 1).value); + + IobTotal iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime); + + double bgi = -iob.activity * sens * 5; + double deviation = delta - bgi; + + List recentTreatments = MainApp.getConfigBuilder().getTreatments5MinBackFromHistory(bgTime); + for (int ir = 0; ir < recentTreatments.size(); ir++) { + autosensData.carbsFromBolus += recentTreatments.get(ir).carbs; + autosensData.activeCarbsList.add(new AutosensData.CarbsInPast(recentTreatments.get(ir))); + } + + + // if we are absorbing carbs + if (previous != null && previous.cob > 0) { + // calculate sum of min carb impact from all active treatments + double totalMinCarbsImpact = 0d; + for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) { + AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii); + totalMinCarbsImpact += c.min5minCarbImpact; + } + + // figure out how many carbs that represents + // but always assume at least 3mg/dL/5m (default) absorption per active treatment + double ci = Math.max(deviation, totalMinCarbsImpact); + autosensData.absorbed = ci * profile.getIc(bgTime) / sens; + // and add that to the running total carbsAbsorbed + autosensData.cob = Math.max(previous.cob - autosensData.absorbed, 0d); + autosensData.substractAbosorbedCarbs(); + } + autosensData.removeOldCarbs(bgTime); + autosensData.cob += autosensData.carbsFromBolus; + autosensData.deviation = deviation; + autosensData.bgi = bgi; + autosensData.delta = delta; + + // calculate autosens only without COB + if (autosensData.cob <= 0) { + if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) { + autosensData.pastSensitivity += "="; + autosensData.nonEqualDeviation = true; + } else if (deviation > 0) { + autosensData.pastSensitivity += "+"; + autosensData.nonEqualDeviation = true; + } else { + autosensData.pastSensitivity += "-"; + autosensData.nonEqualDeviation = true; + } + autosensData.nonCarbsDeviation = true; + } else { + autosensData.pastSensitivity += "C"; + } + //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); + autosensData.autosensRatio = iobCobCalculatorPlugin.detectSensitivity(oldestTimeWithData, bgTime).ratio; + if (Config.logAutosensData) + log.debug(autosensData.log(bgTime)); + } + } + MainApp.bus().post(new EventAutosensCalculationFinished()); + log.debug("Finishing calculation thread: " + from); + } finally { + mWakeLock.release(); + } + } + +}