IobThreads -> kt
This commit is contained in:
parent
a2c0fa7643
commit
32df0b9490
5 changed files with 613 additions and 738 deletions
|
@ -32,11 +32,9 @@ import info.nightscout.androidaps.utils.rx.AapsSchedulers
|
|||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import org.json.JSONArray
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.max
|
||||
|
@ -90,7 +88,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
disposable.add(rxBus
|
||||
.toObservable(EventConfigBuilderChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventConfigBuilderChange? ->
|
||||
.subscribe({ event ->
|
||||
stopCalculation("onEventConfigBuilderChange")
|
||||
synchronized(dataLock) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of configuration change.")
|
||||
|
@ -103,10 +101,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
disposable.add(rxBus
|
||||
.toObservable(EventNewBasalProfile::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventNewBasalProfile? ->
|
||||
if (event == null) { // on init no need of reset
|
||||
return@subscribe
|
||||
}
|
||||
.subscribe({ event ->
|
||||
stopCalculation("onNewProfile")
|
||||
synchronized(dataLock) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Invalidating cached data because of new profile.")
|
||||
|
@ -120,7 +115,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
.toObservable(EventNewBG::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.debounce(1L, TimeUnit.SECONDS)
|
||||
.subscribe({ event: EventNewBG? ->
|
||||
.subscribe({ event ->
|
||||
stopCalculation("onEventNewBG")
|
||||
runCalculation("onEventNewBG", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event)
|
||||
}, fabricPrivacy::logException)
|
||||
|
@ -129,7 +124,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
disposable.add(rxBus
|
||||
.toObservable(EventPreferenceChange::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventPreferenceChange ->
|
||||
.subscribe({ event ->
|
||||
if (event.isChanged(resourceHelper, R.string.key_openapsama_autosens_period) ||
|
||||
event.isChanged(resourceHelper, R.string.key_age) ||
|
||||
event.isChanged(resourceHelper, R.string.key_absorption_maxtime) ||
|
||||
|
@ -151,19 +146,19 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
disposable.add(rxBus
|
||||
.toObservable(EventAppInitialized::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventAppInitialized? -> runCalculation("onEventAppInitialized", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) }, fabricPrivacy::logException)
|
||||
.subscribe({ event -> runCalculation("onEventAppInitialized", System.currentTimeMillis(), bgDataReload = true, limitDataToOldestAvailable = true, cause = event) }, fabricPrivacy::logException)
|
||||
)
|
||||
// EventNewHistoryData
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventNewHistoryData::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventNewHistoryData -> newHistoryData(event, false) }, fabricPrivacy::logException)
|
||||
.subscribe({ event -> newHistoryData(event, false) }, fabricPrivacy::logException)
|
||||
)
|
||||
// EventNewHistoryBgData
|
||||
disposable.add(rxBus
|
||||
.toObservable(EventNewHistoryBgData::class.java)
|
||||
.observeOn(aapsSchedulers.io)
|
||||
.subscribe({ event: EventNewHistoryBgData -> newHistoryData(EventNewHistoryData(event.timestamp), true) }, fabricPrivacy::logException)
|
||||
.subscribe({ event -> newHistoryData(EventNewHistoryData(event.timestamp), true) }, fabricPrivacy::logException)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -691,7 +686,7 @@ open class IobCobCalculatorPlugin @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event?) {
|
||||
fun runCalculation(from: String, end: Long, bgDataReload: Boolean, limitDataToOldestAvailable: Boolean, cause: Event) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Starting calculation thread: " + from + " to " + dateUtil.dateAndTimeAndSecondsString(end))
|
||||
if (thread == null || thread?.state == Thread.State.TERMINATED) {
|
||||
thread = if (sensitivityOref1Plugin.isEnabled()) IobCobOref1Thread(injector, this, treatmentsPlugin, from, end, bgDataReload, limitDataToOldestAvailable, cause) else IobCobThread(injector, this, treatmentsPlugin, from, end, bgDataReload, limitDataToOldestAvailable, cause)
|
||||
|
|
|
@ -1,399 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.collection.LongSparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.IobTotal;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
import info.nightscout.androidaps.db.TempTarget;
|
||||
import info.nightscout.androidaps.db.Treatment;
|
||||
import info.nightscout.androidaps.events.Event;
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter;
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||
import info.nightscout.androidaps.utils.MidnightTime;
|
||||
import info.nightscout.androidaps.utils.Profiler;
|
||||
import info.nightscout.androidaps.utils.T;
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
||||
|
||||
import static info.nightscout.androidaps.utils.DateUtil.now;
|
||||
import static java.util.Calendar.MINUTE;
|
||||
|
||||
/**
|
||||
* Created by mike on 23.01.2018.
|
||||
*/
|
||||
|
||||
public class IobCobOref1Thread extends Thread {
|
||||
private final Event cause;
|
||||
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject SP sp;
|
||||
@Inject RxBusWrapper rxBus;
|
||||
@Inject ResourceHelper resourceHelper;
|
||||
@Inject ProfileFunction profileFunction;
|
||||
@Inject Context context;
|
||||
@Inject SensitivityAAPSPlugin sensitivityAAPSPlugin;
|
||||
@Inject SensitivityWeightedAveragePlugin sensitivityWeightedAveragePlugin;
|
||||
@Inject BuildHelper buildHelper;
|
||||
@Inject Profiler profiler;
|
||||
@Inject FabricPrivacy fabricPrivacy;
|
||||
@Inject DateUtil dateUtil;
|
||||
|
||||
private final HasAndroidInjector injector;
|
||||
private final IobCobCalculatorPlugin iobCobCalculatorPlugin; // cannot be injected : HistoryBrowser uses different instance
|
||||
private final TreatmentsPlugin treatmentsPlugin; // cannot be injected : HistoryBrowser uses different instance
|
||||
private final boolean bgDataReload;
|
||||
private final boolean limitDataToOldestAvailable;
|
||||
private final String from;
|
||||
private final long end;
|
||||
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
IobCobOref1Thread(HasAndroidInjector injector, IobCobCalculatorPlugin iobCobCalculatorPlugin, TreatmentsPlugin treatmentsPlugin, String from, long end, boolean bgDataReload, boolean limitDataToOldestAvailable, Event cause) {
|
||||
super();
|
||||
injector.androidInjector().inject(this);
|
||||
this.injector = injector;
|
||||
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
|
||||
this.treatmentsPlugin = treatmentsPlugin;
|
||||
|
||||
this.bgDataReload = bgDataReload;
|
||||
this.limitDataToOldestAvailable = limitDataToOldestAvailable;
|
||||
this.from = from;
|
||||
this.cause = cause;
|
||||
this.end = end;
|
||||
|
||||
PowerManager powerManager = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
if (powerManager != null)
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, resourceHelper.gs(R.string.app_name) + ":iobCobThread");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
long start = DateUtil.now();
|
||||
if (mWakeLock != null)
|
||||
mWakeLock.acquire(T.mins(10).msecs());
|
||||
try {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: " + from);
|
||||
if (!profileFunction.isProfileValid("IobCobThread")) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): " + from);
|
||||
return; // app still initializing
|
||||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
|
||||
long oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable);
|
||||
|
||||
synchronized (iobCobCalculatorPlugin.getDataLock()) {
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end);
|
||||
iobCobCalculatorPlugin.createBucketedData();
|
||||
rxBus.send(new EventAutosensBgLoaded(cause));
|
||||
}
|
||||
List<InMemoryGlucoseValue> bucketed_data = iobCobCalculatorPlugin.getBucketedData();
|
||||
LongSparseArray<AutosensData> autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable();
|
||||
|
||||
if (bucketed_data == null || bucketed_data.size() < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): " + from);
|
||||
return;
|
||||
}
|
||||
|
||||
long prevDataTime = IobCobCalculatorPlugin.Companion.roundUpTime(bucketed_data.get(bucketed_data.size() - 3).getTimestamp());
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime));
|
||||
AutosensData previous = autosensDataTable.get(prevDataTime);
|
||||
// start from oldest to be able sub cob
|
||||
for (int i = bucketed_data.size() - 4; i >= 0; i--) {
|
||||
String progress = i + (buildHelper.isDev() ? " (" + from + ")" : "");
|
||||
rxBus.send(new EventIobCalculationProgress(progress));
|
||||
|
||||
if (iobCobCalculatorPlugin.getStopCalculationTrigger()) {
|
||||
iobCobCalculatorPlugin.setStopCalculationTrigger(false);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): " + from);
|
||||
return;
|
||||
}
|
||||
// check if data already exists
|
||||
long bgTime = bucketed_data.get(i).getTimestamp();
|
||||
bgTime = IobCobCalculatorPlugin.Companion.roundUpTime(bgTime);
|
||||
if (bgTime > IobCobCalculatorPlugin.Companion.roundUpTime(now()))
|
||||
continue;
|
||||
|
||||
AutosensData existing;
|
||||
if ((existing = autosensDataTable.get(bgTime)) != null) {
|
||||
previous = existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
Profile profile = profileFunction.getProfile(bgTime);
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): " + from);
|
||||
return; // profile not set yet
|
||||
}
|
||||
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")");
|
||||
|
||||
double sens = profile.getIsfMgdl(bgTime);
|
||||
|
||||
AutosensData autosensData = new AutosensData(injector);
|
||||
autosensData.time = bgTime;
|
||||
if (previous != null)
|
||||
autosensData.activeCarbsList = previous.cloneCarbsList();
|
||||
else
|
||||
autosensData.activeCarbsList = new ArrayList<>();
|
||||
|
||||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
double bg;
|
||||
double avgDelta;
|
||||
double delta;
|
||||
bg = bucketed_data.get(i).getValue();
|
||||
if (bg < 39 || bucketed_data.get(i + 3).getValue() < 39) {
|
||||
aapsLogger.error("! value < 39");
|
||||
continue;
|
||||
}
|
||||
autosensData.bg = bg;
|
||||
delta = (bg - bucketed_data.get(i + 1).getValue());
|
||||
avgDelta = (bg - bucketed_data.get(i + 3).getValue()) / 3;
|
||||
|
||||
IobTotal iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile);
|
||||
|
||||
double bgi = -iob.activity * sens * 5;
|
||||
double deviation = delta - bgi;
|
||||
double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000d;
|
||||
|
||||
double slopeFromMaxDeviation = 0;
|
||||
double slopeFromMinDeviation = 999;
|
||||
double maxDeviation = 0;
|
||||
double minDeviation = 999;
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
long hourago = bgTime + 10 * 1000 - 60 * 60 * 1000L;
|
||||
AutosensData hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourago);
|
||||
if (hourAgoData != null) {
|
||||
int initialIndex = autosensDataTable.indexOfKey(hourAgoData.time);
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + " hourAgoData=" + hourAgoData.toString());
|
||||
int past = 1;
|
||||
try {
|
||||
for (; past < 12; past++) {
|
||||
AutosensData ad = autosensDataTable.valueAt(initialIndex + past);
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null));
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString());
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketed_data.toString());
|
||||
aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadings().toString());
|
||||
Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
sp.putBoolean("log_AUTOSENS", true);
|
||||
break;
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5;
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = Math.min(0, deviationSlope);
|
||||
maxDeviation = ad.avgDeviation;
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = Math.max(0, deviationSlope);
|
||||
minDeviation = ad.avgDeviation;
|
||||
}
|
||||
|
||||
//if (Config.isEnabled(L.AUTOSENS))
|
||||
// log.debug("Deviations: " + new Date(bgTime) + new Date(ad.time) + " avgDeviation=" + avgDeviation + " deviationSlope=" + deviationSlope + " slopeFromMaxDeviation=" + slopeFromMaxDeviation + " slopeFromMinDeviation=" + slopeFromMinDeviation);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
fabricPrivacy.logException(e);
|
||||
aapsLogger.debug(autosensDataTable.toString());
|
||||
aapsLogger.debug(bucketed_data.toString());
|
||||
aapsLogger.debug(iobCobCalculatorPlugin.getBgReadings().toString());
|
||||
Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
sp.putBoolean("log_AUTOSENS", true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + " hourAgoData=" + "null");
|
||||
}
|
||||
}
|
||||
|
||||
List<Treatment> recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime);
|
||||
for (Treatment recentCarbTreatment : recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.carbs;
|
||||
boolean isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled();
|
||||
autosensData.activeCarbsList.add(autosensData.new CarbsInPast(recentCarbTreatment, isAAPSOrWeighted));
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]";
|
||||
}
|
||||
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
double totalMinCarbsImpact = 0d;
|
||||
// if (SensitivityAAPSPlugin.getPlugin().isEnabled(PluginType.SENSITIVITY) || SensitivityWeightedAveragePlugin.getPlugin().isEnabled(PluginType.SENSITIVITY)) {
|
||||
//when the impact depends on a max time, sum them up as smaller carb sizes make them smaller
|
||||
// for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) {
|
||||
// AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii);
|
||||
// totalMinCarbsImpact += c.min5minCarbImpact;
|
||||
// }
|
||||
// } else {
|
||||
//Oref sensitivity
|
||||
totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact);
|
||||
// }
|
||||
|
||||
// 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);
|
||||
if (ci != deviation)
|
||||
autosensData.failoverToMinAbsorbtionRate = true;
|
||||
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.mealCarbs = previous.mealCarbs;
|
||||
autosensData.substractAbosorbedCarbs();
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact;
|
||||
autosensData.absorbing = previous.absorbing;
|
||||
autosensData.mealStartCounter = previous.mealStartCounter;
|
||||
autosensData.type = previous.type;
|
||||
autosensData.uam = previous.uam;
|
||||
}
|
||||
|
||||
boolean isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled();
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted);
|
||||
autosensData.cob += autosensData.carbsFromBolus;
|
||||
autosensData.mealCarbs += autosensData.carbsFromBolus;
|
||||
autosensData.deviation = deviation;
|
||||
autosensData.bgi = bgi;
|
||||
autosensData.delta = delta;
|
||||
autosensData.avgDelta = avgDelta;
|
||||
autosensData.avgDeviation = avgDeviation;
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation;
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation;
|
||||
|
||||
|
||||
// If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens
|
||||
if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) {
|
||||
autosensData.absorbing = deviation > 0;
|
||||
// stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h
|
||||
if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) {
|
||||
autosensData.absorbing = false;
|
||||
}
|
||||
if (!autosensData.absorbing && autosensData.cob < 0.5) {
|
||||
autosensData.mealCarbs = 0;
|
||||
}
|
||||
// check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag
|
||||
if (!autosensData.type.equals("csf")) {
|
||||
// process.stderr.write("(");
|
||||
autosensData.mealStartCounter = 0;
|
||||
}
|
||||
autosensData.mealStartCounter++;
|
||||
autosensData.type = "csf";
|
||||
} else {
|
||||
// check previous "type" value, and if it was csf, set a mealAbsorption end flag
|
||||
if (autosensData.type.equals("csf")) {
|
||||
// process.stderr.write(")");
|
||||
}
|
||||
|
||||
double currentBasal = profile.getBasal(bgTime);
|
||||
// always exclude the first 45m after each carb entry
|
||||
//if (iob.iob > currentBasal || uam ) {
|
||||
if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) {
|
||||
autosensData.mealStartCounter++;
|
||||
autosensData.uam = deviation > 0;
|
||||
if (!autosensData.type.equals("uam")) {
|
||||
// process.stderr.write("u(");
|
||||
}
|
||||
autosensData.type = "uam";
|
||||
} else {
|
||||
if (autosensData.type.equals("uam")) {
|
||||
// process.stderr.write(")");
|
||||
}
|
||||
autosensData.type = "non-meal";
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude meal-related deviations (carb absorption) from autosens
|
||||
if (autosensData.type.equals("non-meal")) {
|
||||
if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) {
|
||||
autosensData.pastSensitivity += "=";
|
||||
autosensData.validDeviation = true;
|
||||
} else if (deviation > 0) {
|
||||
autosensData.pastSensitivity += "+";
|
||||
autosensData.validDeviation = true;
|
||||
} else {
|
||||
autosensData.pastSensitivity += "-";
|
||||
autosensData.validDeviation = true;
|
||||
}
|
||||
} else if (autosensData.type.equals("uam")) {
|
||||
autosensData.pastSensitivity += "u";
|
||||
} else {
|
||||
autosensData.pastSensitivity += "x";
|
||||
}
|
||||
//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);
|
||||
|
||||
// add an extra negative deviation if a high temptarget is running and exercise mode is set
|
||||
// TODO AS-FIX
|
||||
if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) {
|
||||
TempTarget tempTarget = treatmentsPlugin.getTempTargetFromHistory(bgTime);
|
||||
if (tempTarget != null && tempTarget.target() >= 100) {
|
||||
autosensData.extraDeviation.add(-(tempTarget.target() - 100) / 20);
|
||||
}
|
||||
}
|
||||
|
||||
// add one neutral deviation every 2 hours to help decay over long exclusion periods
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
calendar.setTimeInMillis(bgTime);
|
||||
int min = calendar.get(MINUTE);
|
||||
int hours = calendar.get(Calendar.HOUR_OF_DAY);
|
||||
if (min >= 0 && min < 5 && hours % 2 == 0)
|
||||
autosensData.extraDeviation.add(0d);
|
||||
|
||||
previous = autosensData;
|
||||
if (bgTime < now())
|
||||
autosensDataTable.put(bgTime, autosensData);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime());
|
||||
AutosensResult sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: " + sensitivity.toString());
|
||||
autosensData.autosensResult = sensitivity;
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString());
|
||||
}
|
||||
}
|
||||
new Thread(() -> {
|
||||
SystemClock.sleep(1000);
|
||||
rxBus.send(new EventAutosensCalculationFinished(cause));
|
||||
}).start();
|
||||
} finally {
|
||||
if (mWakeLock != null)
|
||||
mWakeLock.release();
|
||||
rxBus.send(new EventIobCalculationProgress(""));
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: " + from);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log());
|
||||
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
|
||||
|
||||
import android.content.Context
|
||||
import android.os.PowerManager
|
||||
import android.os.SystemClock
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.roundUpTime
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
class IobCobOref1Thread internal constructor(
|
||||
private val injector: HasAndroidInjector,
|
||||
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance
|
||||
private val treatmentsPlugin: TreatmentsPlugin, // cannot be injected : HistoryBrowser uses different instance
|
||||
private val from: String,
|
||||
private val end: Long,
|
||||
private val bgDataReload: Boolean,
|
||||
private val limitDataToOldestAvailable: Boolean,
|
||||
private val cause: Event
|
||||
) : Thread() {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var profiler: Profiler
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
|
||||
private var mWakeLock: PowerManager.WakeLock? = null
|
||||
|
||||
init {
|
||||
injector.androidInjector().inject(this)
|
||||
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, resourceHelper.gs(R.string.app_name) + ":iobCobThread")
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
val start = DateUtil.now()
|
||||
mWakeLock?.acquire(T.mins(10).msecs())
|
||||
try {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from")
|
||||
if (!profileFunction.isProfileValid("IobCobThread")) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from")
|
||||
return // app still initializing
|
||||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||
synchronized(iobCobCalculatorPlugin.dataLock) {
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
rxBus.send(EventAutosensBgLoaded(cause))
|
||||
}
|
||||
val bucketedData = iobCobCalculatorPlugin.bucketedData
|
||||
val autosensDataTable = iobCobCalculatorPlugin.autosensDataTable
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
return
|
||||
}
|
||||
val prevDataTime = roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
return
|
||||
}
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = roundUpTime(bgTime)
|
||||
if (bgTime > roundUpTime(DateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
|
||||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
var avgDelta: Double
|
||||
var delta: Double
|
||||
val bg: Double = bucketedData[i].value
|
||||
if (bg < 39 || bucketedData[i + 3].value < 39) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.bgReadings.toString())
|
||||
val notification = Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
aapsLogger.debug(iobCobCalculatorPlugin.bgReadings.toString())
|
||||
val notification = Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
}
|
||||
val recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime)
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.carbs
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
val totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.mealCarbs = previous.mealCarbs
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
autosensData.absorbing = previous.absorbing
|
||||
autosensData.mealStartCounter = previous.mealStartCounter
|
||||
autosensData.type = previous.type
|
||||
autosensData.uam = previous.uam
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.mealCarbs += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// If mealCOB is zero but all deviations since hitting COB=0 are positive, exclude from autosens
|
||||
if (autosensData.cob > 0 || autosensData.absorbing || autosensData.mealCarbs > 0) {
|
||||
autosensData.absorbing = deviation > 0
|
||||
// stop excluding positive deviations as soon as mealCOB=0 if meal has been absorbing for >5h
|
||||
if (autosensData.mealStartCounter > 60 && autosensData.cob < 0.5) {
|
||||
autosensData.absorbing = false
|
||||
}
|
||||
if (!autosensData.absorbing && autosensData.cob < 0.5) {
|
||||
autosensData.mealCarbs = 0.0
|
||||
}
|
||||
// check previous "type" value, and if it wasn't csf, set a mealAbsorption start flag
|
||||
if (autosensData.type != "csf") {
|
||||
// process.stderr.write("(");
|
||||
autosensData.mealStartCounter = 0
|
||||
}
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.type = "csf"
|
||||
} else {
|
||||
// check previous "type" value, and if it was csf, set a mealAbsorption end flag
|
||||
val currentBasal = profile.getBasal(bgTime)
|
||||
// always exclude the first 45m after each carb entry
|
||||
//if (iob.iob > currentBasal || uam ) {
|
||||
if (iob.iob > 2 * currentBasal || autosensData.uam || autosensData.mealStartCounter < 9) {
|
||||
autosensData.mealStartCounter++
|
||||
autosensData.uam = deviation > 0
|
||||
autosensData.type = "uam"
|
||||
} else {
|
||||
autosensData.type = "non-meal"
|
||||
}
|
||||
}
|
||||
|
||||
// Exclude meal-related deviations (carb absorption) from autosens
|
||||
when (autosensData.type) {
|
||||
"non-meal" -> {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"uam" -> {
|
||||
autosensData.pastSensitivity += "u"
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "x"
|
||||
}
|
||||
}
|
||||
|
||||
// add an extra negative deviation if a high temptarget is running and exercise mode is set
|
||||
// TODO AS-FIX
|
||||
@Suppress("SimplifyBooleanWithConstants")
|
||||
if (false && sp.getBoolean(R.string.key_high_temptarget_raises_sensitivity, SMBDefaults.high_temptarget_raises_sensitivity)) {
|
||||
val tempTarget = treatmentsPlugin.getTempTargetFromHistory(bgTime)
|
||||
if (tempTarget != null && tempTarget.target() >= 100) {
|
||||
autosensData.extraDeviation.add(-(tempTarget.target() - 100) / 20)
|
||||
}
|
||||
}
|
||||
|
||||
// add one neutral deviation every 2 hours to help decay over long exclusion periods
|
||||
val calendar = GregorianCalendar()
|
||||
calendar.timeInMillis = bgTime
|
||||
val min = calendar[Calendar.MINUTE]
|
||||
val hours = calendar[Calendar.HOUR_OF_DAY]
|
||||
if (min in 0..4 && hours % 2 == 0) autosensData.extraDeviation.add(0.0)
|
||||
previous = autosensData
|
||||
if (bgTime < DateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime())
|
||||
val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
}
|
||||
}
|
||||
Thread {
|
||||
SystemClock.sleep(1000)
|
||||
rxBus.send(EventAutosensCalculationFinished(cause))
|
||||
}.start()
|
||||
} finally {
|
||||
mWakeLock?.release()
|
||||
rxBus.send(EventIobCalculationProgress(""))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log())
|
||||
profiler.log(LTag.AUTOSENS, "IobCobOref1Thread", start)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,326 +0,0 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import androidx.collection.LongSparseArray;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import info.nightscout.androidaps.Constants;
|
||||
import info.nightscout.androidaps.R;
|
||||
import info.nightscout.androidaps.data.IobTotal;
|
||||
import info.nightscout.androidaps.data.Profile;
|
||||
import info.nightscout.androidaps.db.Treatment;
|
||||
import info.nightscout.androidaps.events.Event;
|
||||
import info.nightscout.androidaps.interfaces.PluginType;
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction;
|
||||
import info.nightscout.androidaps.logging.AAPSLogger;
|
||||
import info.nightscout.androidaps.logging.LTag;
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults;
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper;
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification;
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished;
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin;
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin;
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin;
|
||||
import info.nightscout.androidaps.utils.DateUtil;
|
||||
import info.nightscout.androidaps.utils.DecimalFormatter;
|
||||
import info.nightscout.androidaps.utils.FabricPrivacy;
|
||||
import info.nightscout.androidaps.utils.MidnightTime;
|
||||
import info.nightscout.androidaps.utils.Profiler;
|
||||
import info.nightscout.androidaps.utils.T;
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper;
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
||||
|
||||
import static info.nightscout.androidaps.utils.DateUtil.now;
|
||||
|
||||
/**
|
||||
* Created by mike on 23.01.2018.
|
||||
*/
|
||||
|
||||
public class IobCobThread extends Thread {
|
||||
private final Event cause;
|
||||
|
||||
@Inject AAPSLogger aapsLogger;
|
||||
@Inject SP sp;
|
||||
@Inject RxBusWrapper rxBus;
|
||||
@Inject ResourceHelper resourceHelper;
|
||||
@Inject ProfileFunction profileFunction;
|
||||
@Inject Context context;
|
||||
@Inject SensitivityAAPSPlugin sensitivityAAPSPlugin;
|
||||
@Inject SensitivityWeightedAveragePlugin sensitivityWeightedAveragePlugin;
|
||||
@Inject BuildHelper buildHelper;
|
||||
@Inject Profiler profiler;
|
||||
@Inject FabricPrivacy fabricPrivacy;
|
||||
@Inject DateUtil dateUtil;
|
||||
|
||||
private final HasAndroidInjector injector;
|
||||
private final IobCobCalculatorPlugin iobCobCalculatorPlugin; // cannot be injected : HistoryBrowser uses different instance
|
||||
private final TreatmentsPlugin treatmentsPlugin; // cannot be injected : HistoryBrowser uses different instance
|
||||
private final boolean bgDataReload;
|
||||
private final boolean limitDataToOldestAvailable;
|
||||
private final String from;
|
||||
private final long end;
|
||||
|
||||
private PowerManager.WakeLock mWakeLock;
|
||||
|
||||
@Inject IobCobThread(HasAndroidInjector injector, IobCobCalculatorPlugin iobCobCalculatorPlugin, TreatmentsPlugin treatmentsPlugin, String from, long end, boolean bgDataReload, boolean limitDataToOldestAvailable, Event cause) {
|
||||
super();
|
||||
injector.androidInjector().inject(this);
|
||||
this.injector = injector;
|
||||
this.iobCobCalculatorPlugin = iobCobCalculatorPlugin;
|
||||
this.treatmentsPlugin = treatmentsPlugin;
|
||||
|
||||
this.bgDataReload = bgDataReload;
|
||||
this.limitDataToOldestAvailable = limitDataToOldestAvailable;
|
||||
this.from = from;
|
||||
this.cause = cause;
|
||||
this.end = end;
|
||||
|
||||
PowerManager powerManager = (PowerManager) context.getApplicationContext().getSystemService(Context.POWER_SERVICE);
|
||||
if (powerManager != null)
|
||||
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, resourceHelper.gs(R.string.app_name) + ":iobCobThread");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
long start = DateUtil.now();
|
||||
if (mWakeLock != null)
|
||||
mWakeLock.acquire(T.mins(10).msecs());
|
||||
try {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: " + from);
|
||||
if (!profileFunction.isProfileValid("IobCobThread")) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): " + from);
|
||||
return; // app still initializing
|
||||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
|
||||
long oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable);
|
||||
|
||||
synchronized (iobCobCalculatorPlugin.getDataLock()) {
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end);
|
||||
iobCobCalculatorPlugin.createBucketedData();
|
||||
rxBus.send(new EventAutosensBgLoaded(cause));
|
||||
}
|
||||
List<InMemoryGlucoseValue> bucketed_data = iobCobCalculatorPlugin.getBucketedData();
|
||||
LongSparseArray<AutosensData> autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable();
|
||||
|
||||
if (bucketed_data == null || bucketed_data.size() < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): " + from);
|
||||
return;
|
||||
}
|
||||
|
||||
long prevDataTime = IobCobCalculatorPlugin.Companion.roundUpTime(bucketed_data.get(bucketed_data.size() - 3).getTimestamp());
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime));
|
||||
AutosensData previous = autosensDataTable.get(prevDataTime);
|
||||
// start from oldest to be able sub cob
|
||||
for (int i = bucketed_data.size() - 4; i >= 0; i--) {
|
||||
String progress = i + (buildHelper.isDev() ? " (" + from + ")" : "");
|
||||
rxBus.send(new EventIobCalculationProgress(progress));
|
||||
|
||||
if (iobCobCalculatorPlugin.getStopCalculationTrigger()) {
|
||||
iobCobCalculatorPlugin.setStopCalculationTrigger(false);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): " + from);
|
||||
return;
|
||||
}
|
||||
// check if data already exists
|
||||
long bgTime = bucketed_data.get(i).getTimestamp();
|
||||
bgTime = IobCobCalculatorPlugin.Companion.roundUpTime(bgTime);
|
||||
if (bgTime > IobCobCalculatorPlugin.Companion.roundUpTime(now()))
|
||||
continue;
|
||||
|
||||
AutosensData existing;
|
||||
if ((existing = autosensDataTable.get(bgTime)) != null) {
|
||||
previous = existing;
|
||||
continue;
|
||||
}
|
||||
|
||||
Profile profile = profileFunction.getProfile(bgTime);
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): " + from);
|
||||
return; // profile not set yet
|
||||
}
|
||||
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketed_data.size() + ")");
|
||||
|
||||
double sens = profile.getIsfMgdl(bgTime);
|
||||
|
||||
AutosensData autosensData = new AutosensData(injector);
|
||||
autosensData.time = bgTime;
|
||||
if (previous != null)
|
||||
autosensData.activeCarbsList = previous.cloneCarbsList();
|
||||
else
|
||||
autosensData.activeCarbsList = new ArrayList<>();
|
||||
|
||||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
double bg;
|
||||
double avgDelta;
|
||||
double delta;
|
||||
bg = bucketed_data.get(i).getValue();
|
||||
if (bg < 39 || bucketed_data.get(i + 3).getValue() < 39) {
|
||||
aapsLogger.error("! value < 39");
|
||||
continue;
|
||||
}
|
||||
autosensData.bg = bg;
|
||||
delta = (bg - bucketed_data.get(i + 1).getValue());
|
||||
avgDelta = (bg - bucketed_data.get(i + 3).getValue()) / 3;
|
||||
|
||||
IobTotal iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile);
|
||||
|
||||
double bgi = -iob.activity * sens * 5;
|
||||
double deviation = delta - bgi;
|
||||
double avgDeviation = Math.round((avgDelta - bgi) * 1000) / 1000d;
|
||||
|
||||
double slopeFromMaxDeviation = 0;
|
||||
double slopeFromMinDeviation = 999;
|
||||
double maxDeviation = 0;
|
||||
double minDeviation = 999;
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketed_data.size() - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
long hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L;
|
||||
AutosensData hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo);
|
||||
if (hourAgoData != null) {
|
||||
int initialIndex = autosensDataTable.indexOfKey(hourAgoData.time);
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + " hourAgoData=" + hourAgoData.toString());
|
||||
int past = 1;
|
||||
try {
|
||||
for (; past < 12; past++) {
|
||||
AutosensData ad = autosensDataTable.valueAt(initialIndex + past);
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + (ad != null ? ad.toString() : null));
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString());
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketed_data.toString());
|
||||
aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.getBgReadings().toString());
|
||||
Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
sp.putBoolean("log_AUTOSENS", true);
|
||||
break;
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
double deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5;
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = Math.min(0, deviationSlope);
|
||||
maxDeviation = ad.avgDeviation;
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = Math.max(0, deviationSlope);
|
||||
minDeviation = ad.avgDeviation;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
aapsLogger.error("Unhandled exception", e);
|
||||
fabricPrivacy.logException(e);
|
||||
aapsLogger.debug(autosensDataTable.toString());
|
||||
aapsLogger.debug(bucketed_data.toString());
|
||||
aapsLogger.debug(iobCobCalculatorPlugin.getBgReadings().toString());
|
||||
Notification notification = new Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW);
|
||||
rxBus.send(new EventNewNotification(notification));
|
||||
sp.putBoolean("log_AUTOSENS", true);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketed_data.size() + " i=" + i + " hourAgoData=" + "null");
|
||||
}
|
||||
}
|
||||
|
||||
List<Treatment> recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime);
|
||||
for (Treatment recentCarbTreatment : recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.carbs;
|
||||
boolean isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled();
|
||||
autosensData.activeCarbsList.add(autosensData.new CarbsInPast(recentCarbTreatment, isAAPSOrWeighted));
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]";
|
||||
}
|
||||
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
double totalMinCarbsImpact = 0d;
|
||||
if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) {
|
||||
//when the impact depends on a max time, sum them up as smaller carb sizes make them smaller
|
||||
for (int ii = 0; ii < autosensData.activeCarbsList.size(); ++ii) {
|
||||
AutosensData.CarbsInPast c = autosensData.activeCarbsList.get(ii);
|
||||
totalMinCarbsImpact += c.min5minCarbImpact;
|
||||
}
|
||||
} else {
|
||||
//Oref sensitivity
|
||||
totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact);
|
||||
}
|
||||
|
||||
// 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);
|
||||
if (ci != deviation)
|
||||
autosensData.failoverToMinAbsorbtionRate = true;
|
||||
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.usedMinCarbsImpact = totalMinCarbsImpact;
|
||||
}
|
||||
boolean isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled();
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted);
|
||||
autosensData.cob += autosensData.carbsFromBolus;
|
||||
autosensData.deviation = deviation;
|
||||
autosensData.bgi = bgi;
|
||||
autosensData.delta = delta;
|
||||
autosensData.avgDelta = avgDelta;
|
||||
autosensData.avgDeviation = avgDeviation;
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation;
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation;
|
||||
|
||||
|
||||
// calculate autosens only without COB
|
||||
if (autosensData.cob <= 0) {
|
||||
if (Math.abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL) {
|
||||
autosensData.pastSensitivity += "=";
|
||||
autosensData.validDeviation = true;
|
||||
} else if (deviation > 0) {
|
||||
autosensData.pastSensitivity += "+";
|
||||
autosensData.validDeviation = true;
|
||||
} else {
|
||||
autosensData.pastSensitivity += "-";
|
||||
autosensData.validDeviation = true;
|
||||
}
|
||||
} else {
|
||||
autosensData.pastSensitivity += "C";
|
||||
}
|
||||
|
||||
previous = autosensData;
|
||||
if (bgTime < now())
|
||||
autosensDataTable.put(bgTime, autosensData);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime());
|
||||
AutosensResult sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: " + sensitivity.toString());
|
||||
autosensData.autosensResult = sensitivity;
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString());
|
||||
}
|
||||
}
|
||||
new Thread(() -> {
|
||||
SystemClock.sleep(1000);
|
||||
rxBus.send(new EventAutosensCalculationFinished(cause));
|
||||
}).start();
|
||||
} finally {
|
||||
if (mWakeLock != null)
|
||||
mWakeLock.release();
|
||||
rxBus.send(new EventIobCalculationProgress(""));
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: " + from);
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log());
|
||||
profiler.log(LTag.AUTOSENS, "IobCobThread", start);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
package info.nightscout.androidaps.plugins.iob.iobCobCalculator
|
||||
|
||||
import android.content.Context
|
||||
import android.os.PowerManager
|
||||
import android.os.SystemClock
|
||||
import dagger.android.HasAndroidInjector
|
||||
import info.nightscout.androidaps.Constants
|
||||
import info.nightscout.androidaps.R
|
||||
import info.nightscout.androidaps.events.Event
|
||||
import info.nightscout.androidaps.interfaces.PluginType
|
||||
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||
import info.nightscout.androidaps.logging.AAPSLogger
|
||||
import info.nightscout.androidaps.logging.LTag
|
||||
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
|
||||
import info.nightscout.androidaps.plugins.bus.RxBusWrapper
|
||||
import info.nightscout.androidaps.plugins.general.overview.events.EventNewNotification
|
||||
import info.nightscout.androidaps.plugins.general.overview.notifications.Notification
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.roundUpTime
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensBgLoaded
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventAutosensCalculationFinished
|
||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.events.EventIobCalculationProgress
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityAAPSPlugin
|
||||
import info.nightscout.androidaps.plugins.sensitivity.SensitivityWeightedAveragePlugin
|
||||
import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
|
||||
import info.nightscout.androidaps.utils.*
|
||||
import info.nightscout.androidaps.utils.buildHelper.BuildHelper
|
||||
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
class IobCobThread @Inject internal constructor(
|
||||
private val injector: HasAndroidInjector,
|
||||
private val iobCobCalculatorPlugin: IobCobCalculatorPlugin, // cannot be injected : HistoryBrowser uses different instance
|
||||
private val treatmentsPlugin: TreatmentsPlugin, // cannot be injected : HistoryBrowser uses different instance
|
||||
private val from: String,
|
||||
private val end: Long,
|
||||
private val bgDataReload: Boolean,
|
||||
private val limitDataToOldestAvailable: Boolean,
|
||||
private val cause: Event
|
||||
) : Thread() {
|
||||
|
||||
@Inject lateinit var aapsLogger: AAPSLogger
|
||||
@Inject lateinit var sp: SP
|
||||
@Inject lateinit var rxBus: RxBusWrapper
|
||||
@Inject lateinit var resourceHelper: ResourceHelper
|
||||
@Inject lateinit var profileFunction: ProfileFunction
|
||||
@Inject lateinit var context: Context
|
||||
@Inject lateinit var sensitivityAAPSPlugin: SensitivityAAPSPlugin
|
||||
@Inject lateinit var sensitivityWeightedAveragePlugin: SensitivityWeightedAveragePlugin
|
||||
@Inject lateinit var buildHelper: BuildHelper
|
||||
@Inject lateinit var profiler: Profiler
|
||||
@Inject lateinit var fabricPrivacy: FabricPrivacy
|
||||
@Inject lateinit var dateUtil: DateUtil
|
||||
|
||||
private var mWakeLock: PowerManager.WakeLock? = null
|
||||
|
||||
init {
|
||||
injector.androidInjector().inject(this)
|
||||
mWakeLock = (context.applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, resourceHelper.gs(R.string.app_name) + ":iobCobThread")
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
val start = DateUtil.now()
|
||||
mWakeLock?.acquire(T.mins(10).msecs())
|
||||
try {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread started: $from")
|
||||
if (!profileFunction.isProfileValid("IobCobThread")) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No profile): $from")
|
||||
return // app still initializing
|
||||
}
|
||||
//log.debug("Locking calculateSensitivityData");
|
||||
val oldestTimeWithData = iobCobCalculatorPlugin.calculateDetectionStart(end, limitDataToOldestAvailable)
|
||||
synchronized(iobCobCalculatorPlugin.dataLock) {
|
||||
if (bgDataReload) {
|
||||
iobCobCalculatorPlugin.loadBgData(end)
|
||||
iobCobCalculatorPlugin.createBucketedData()
|
||||
rxBus.send(EventAutosensBgLoaded(cause))
|
||||
}
|
||||
val bucketedData = iobCobCalculatorPlugin.bucketedData
|
||||
val autosensDataTable = iobCobCalculatorPlugin.autosensDataTable
|
||||
if (bucketedData == null || bucketedData.size < 3) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (No bucketed data available): $from")
|
||||
return
|
||||
}
|
||||
val prevDataTime = roundUpTime(bucketedData[bucketedData.size - 3].timestamp)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Prev data time: " + dateUtil.dateAndTimeString(prevDataTime))
|
||||
var previous = autosensDataTable[prevDataTime]
|
||||
// start from oldest to be able sub cob
|
||||
for (i in bucketedData.size - 4 downTo 0) {
|
||||
val progress = i.toString() + if (buildHelper.isDev()) " ($from)" else ""
|
||||
rxBus.send(EventIobCalculationProgress(progress))
|
||||
if (iobCobCalculatorPlugin.stopCalculationTrigger) {
|
||||
iobCobCalculatorPlugin.stopCalculationTrigger = false
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (trigger): $from")
|
||||
return
|
||||
}
|
||||
// check if data already exists
|
||||
var bgTime = bucketedData[i].timestamp
|
||||
bgTime = roundUpTime(bgTime)
|
||||
if (bgTime > roundUpTime(DateUtil.now())) continue
|
||||
var existing: AutosensData?
|
||||
if (autosensDataTable[bgTime].also { existing = it } != null) {
|
||||
previous = existing
|
||||
continue
|
||||
}
|
||||
val profile = profileFunction.getProfile(bgTime)
|
||||
if (profile == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Aborting calculation thread (no profile): $from")
|
||||
return // profile not set yet
|
||||
}
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Processing calculation thread: " + from + " (" + i + "/" + bucketedData.size + ")")
|
||||
val sens = profile.getIsfMgdl(bgTime)
|
||||
val autosensData = AutosensData(injector)
|
||||
autosensData.time = bgTime
|
||||
if (previous != null) autosensData.activeCarbsList = previous.cloneCarbsList() else autosensData.activeCarbsList = ArrayList()
|
||||
|
||||
//console.error(bgTime , bucketed_data[i].glucose);
|
||||
var avgDelta: Double
|
||||
var delta: Double
|
||||
val bg: Double = bucketedData[i].value
|
||||
if (bg < 39 || bucketedData[i + 3].value < 39) {
|
||||
aapsLogger.error("! value < 39")
|
||||
continue
|
||||
}
|
||||
autosensData.bg = bg
|
||||
delta = bg - bucketedData[i + 1].value
|
||||
avgDelta = (bg - bucketedData[i + 3].value) / 3
|
||||
val iob = iobCobCalculatorPlugin.calculateFromTreatmentsAndTemps(bgTime, profile)
|
||||
val bgi = -iob.activity * sens * 5
|
||||
val deviation = delta - bgi
|
||||
val avgDeviation = ((avgDelta - bgi) * 1000).roundToLong() / 1000.0
|
||||
var slopeFromMaxDeviation = 0.0
|
||||
var slopeFromMinDeviation = 999.0
|
||||
|
||||
// https://github.com/openaps/oref0/blob/master/lib/determine-basal/cob-autosens.js#L169
|
||||
if (i < bucketedData.size - 16) { // we need 1h of data to calculate minDeviationSlope
|
||||
@Suppress("UNUSED_VARIABLE") var maxDeviation = 0.0
|
||||
@Suppress("UNUSED_VARIABLE") var minDeviation = 999.0
|
||||
val hourAgo = bgTime + 10 * 1000 - 60 * 60 * 1000L
|
||||
val hourAgoData = iobCobCalculatorPlugin.getAutosensData(hourAgo)
|
||||
if (hourAgoData != null) {
|
||||
val initialIndex = autosensDataTable.indexOfKey(hourAgoData.time)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + hourAgoData.toString())
|
||||
var past = 1
|
||||
try {
|
||||
while (past < 12) {
|
||||
val ad = autosensDataTable.valueAt(initialIndex + past)
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> past=" + past + " ad=" + ad?.toString())
|
||||
if (ad == null) {
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensDataTable.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, bucketedData.toString())
|
||||
aapsLogger.debug(LTag.AUTOSENS, iobCobCalculatorPlugin.bgReadings.toString())
|
||||
val notification = Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
// let it here crash on NPE to get more data as i cannot reproduce this bug
|
||||
val deviationSlope = (ad.avgDeviation - avgDeviation) / (ad.time - bgTime) * 1000 * 60 * 5
|
||||
if (ad.avgDeviation > maxDeviation) {
|
||||
slopeFromMaxDeviation = min(0.0, deviationSlope)
|
||||
maxDeviation = ad.avgDeviation
|
||||
}
|
||||
if (ad.avgDeviation < minDeviation) {
|
||||
slopeFromMinDeviation = max(0.0, deviationSlope)
|
||||
minDeviation = ad.avgDeviation
|
||||
}
|
||||
past++
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
aapsLogger.error("Unhandled exception", e)
|
||||
fabricPrivacy.logException(e)
|
||||
aapsLogger.debug(autosensDataTable.toString())
|
||||
aapsLogger.debug(bucketedData.toString())
|
||||
aapsLogger.debug(iobCobCalculatorPlugin.bgReadings.toString())
|
||||
val notification = Notification(Notification.SENDLOGFILES, resourceHelper.gs(R.string.sendlogfiles), Notification.LOW)
|
||||
rxBus.send(EventNewNotification(notification))
|
||||
sp.putBoolean("log_AUTOSENS", true)
|
||||
break
|
||||
}
|
||||
} else {
|
||||
aapsLogger.debug(LTag.AUTOSENS, ">>>>> bucketed_data.size()=" + bucketedData.size + " i=" + i + " hourAgoData=" + "null")
|
||||
}
|
||||
}
|
||||
val recentCarbTreatments = treatmentsPlugin.getCarbTreatments5MinBackFromHistory(bgTime)
|
||||
for (recentCarbTreatment in recentCarbTreatments) {
|
||||
autosensData.carbsFromBolus += recentCarbTreatment.carbs
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.activeCarbsList.add(autosensData.CarbsInPast(recentCarbTreatment, isAAPSOrWeighted))
|
||||
autosensData.pastSensitivity += "[" + DecimalFormatter.to0Decimal(recentCarbTreatment.carbs) + "g]"
|
||||
}
|
||||
|
||||
// if we are absorbing carbs
|
||||
if (previous != null && previous.cob > 0) {
|
||||
// calculate sum of min carb impact from all active treatments
|
||||
var totalMinCarbsImpact = 0.0
|
||||
if (sensitivityAAPSPlugin.isEnabled(PluginType.SENSITIVITY) || sensitivityWeightedAveragePlugin.isEnabled(PluginType.SENSITIVITY)) {
|
||||
//when the impact depends on a max time, sum them up as smaller carb sizes make them smaller
|
||||
for (ii in autosensData.activeCarbsList.indices) {
|
||||
val c = autosensData.activeCarbsList[ii]
|
||||
totalMinCarbsImpact += c.min5minCarbImpact
|
||||
}
|
||||
} else {
|
||||
//Oref sensitivity
|
||||
totalMinCarbsImpact = sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact)
|
||||
}
|
||||
|
||||
// figure out how many carbs that represents
|
||||
// but always assume at least 3mg/dL/5m (default) absorption per active treatment
|
||||
val ci = max(deviation, totalMinCarbsImpact)
|
||||
if (ci != deviation) autosensData.failoverToMinAbsorbtionRate = true
|
||||
autosensData.absorbed = ci * profile.getIc(bgTime) / sens
|
||||
// and add that to the running total carbsAbsorbed
|
||||
autosensData.cob = max(previous.cob - autosensData.absorbed, 0.0)
|
||||
autosensData.substractAbosorbedCarbs()
|
||||
autosensData.usedMinCarbsImpact = totalMinCarbsImpact
|
||||
}
|
||||
val isAAPSOrWeighted = sensitivityAAPSPlugin.isEnabled() || sensitivityWeightedAveragePlugin.isEnabled()
|
||||
autosensData.removeOldCarbs(bgTime, isAAPSOrWeighted)
|
||||
autosensData.cob += autosensData.carbsFromBolus
|
||||
autosensData.deviation = deviation
|
||||
autosensData.bgi = bgi
|
||||
autosensData.delta = delta
|
||||
autosensData.avgDelta = avgDelta
|
||||
autosensData.avgDeviation = avgDeviation
|
||||
autosensData.slopeFromMaxDeviation = slopeFromMaxDeviation
|
||||
autosensData.slopeFromMinDeviation = slopeFromMinDeviation
|
||||
|
||||
// calculate autosens only without COB
|
||||
if (autosensData.cob <= 0) {
|
||||
when {
|
||||
abs(deviation) < Constants.DEVIATION_TO_BE_EQUAL -> {
|
||||
autosensData.pastSensitivity += "="
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
deviation > 0 -> {
|
||||
autosensData.pastSensitivity += "+"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
autosensData.pastSensitivity += "-"
|
||||
autosensData.validDeviation = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
autosensData.pastSensitivity += "C"
|
||||
}
|
||||
previous = autosensData
|
||||
if (bgTime < DateUtil.now()) autosensDataTable.put(bgTime, autosensData)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Running detectSensitivity from: " + dateUtil.dateAndTimeString(oldestTimeWithData) + " to: " + dateUtil.dateAndTimeString(bgTime) + " lastDataTime:" + iobCobCalculatorPlugin.lastDataTime())
|
||||
val sensitivity = iobCobCalculatorPlugin.detectSensitivityWithLock(oldestTimeWithData, bgTime)
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity result: $sensitivity")
|
||||
autosensData.autosensResult = sensitivity
|
||||
aapsLogger.debug(LTag.AUTOSENS, autosensData.toString())
|
||||
}
|
||||
}
|
||||
Thread {
|
||||
SystemClock.sleep(1000)
|
||||
rxBus.send(EventAutosensCalculationFinished(cause))
|
||||
}.start()
|
||||
} finally {
|
||||
mWakeLock?.release()
|
||||
rxBus.send(EventIobCalculationProgress(""))
|
||||
aapsLogger.debug(LTag.AUTOSENS, "AUTOSENSDATA thread ended: $from")
|
||||
aapsLogger.debug(LTag.AUTOSENS, "Midnights: " + MidnightTime.log())
|
||||
profiler.log(LTag.AUTOSENS, "IobCobThread", start)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue