Sensitivity plugins -> kt
This commit is contained in:
parent
951b35eecb
commit
e6f229b15b
6 changed files with 570 additions and 746 deletions
|
@ -1,220 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.sensitivity;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.collection.LongSparseArray;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.android.HasAndroidInjector;
|
|
||||||
import info.nightscout.androidaps.Constants;
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
|
||||||
import info.nightscout.androidaps.R;
|
|
||||||
import info.nightscout.androidaps.data.Profile;
|
|
||||||
import info.nightscout.androidaps.db.CareportalEvent;
|
|
||||||
import info.nightscout.androidaps.db.ProfileSwitch;
|
|
||||||
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface;
|
|
||||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
|
||||||
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.iob.iobCobCalculator.AutosensResult;
|
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
|
|
||||||
import info.nightscout.androidaps.utils.DateUtil;
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mike on 24.06.2017.
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class SensitivityAAPSPlugin extends AbstractSensitivityPlugin {
|
|
||||||
|
|
||||||
private final ProfileFunction profileFunction;
|
|
||||||
private final DateUtil dateUtil;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SensitivityAAPSPlugin(
|
|
||||||
HasAndroidInjector injector,
|
|
||||||
AAPSLogger aapsLogger,
|
|
||||||
ResourceHelper resourceHelper,
|
|
||||||
SP sp,
|
|
||||||
ProfileFunction profileFunction,
|
|
||||||
DateUtil dateUtil
|
|
||||||
) {
|
|
||||||
super(new PluginDescription()
|
|
||||||
.mainType(PluginType.SENSITIVITY)
|
|
||||||
.pluginIcon(R.drawable.ic_generic_icon)
|
|
||||||
.pluginName(R.string.sensitivityaaps)
|
|
||||||
.shortName(R.string.sensitivity_shortname)
|
|
||||||
.preferencesId(R.xml.pref_absorption_aaps)
|
|
||||||
.description(R.string.description_sensitivity_aaps),
|
|
||||||
injector, aapsLogger, resourceHelper, sp
|
|
||||||
);
|
|
||||||
this.profileFunction = profileFunction;
|
|
||||||
this.dateUtil = dateUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override
|
|
||||||
public AutosensResult detectSensitivity(IobCobCalculatorInterface iobCobCalculatorPlugin, long fromTime, long toTime) {
|
|
||||||
LongSparseArray<AutosensData> autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable();
|
|
||||||
|
|
||||||
String age = getSp().getString(R.string.key_age, "");
|
|
||||||
int defaultHours = 24;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_adult))) defaultHours = 24;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_teenage))) defaultHours = 4;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_child))) defaultHours = 4;
|
|
||||||
int hoursForDetection = getSp().getInt(R.string.key_openapsama_autosens_period, defaultHours);
|
|
||||||
|
|
||||||
Profile profile = profileFunction.getProfile();
|
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
getAapsLogger().error("No profile");
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensDataTable == null || autosensDataTable.size() < 4) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
AutosensData current = iobCobCalculatorPlugin.getAutosensData(toTime); // this is running inside lock already
|
|
||||||
if (current == null) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
List<CareportalEvent> siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true);
|
|
||||||
List<ProfileSwitch> profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true);
|
|
||||||
|
|
||||||
List<Double> deviationsArray = new ArrayList<>();
|
|
||||||
String pastSensitivity = "";
|
|
||||||
int index = 0;
|
|
||||||
while (index < autosensDataTable.size()) {
|
|
||||||
AutosensData autosensData = autosensDataTable.valueAt(index);
|
|
||||||
|
|
||||||
if (autosensData.time < fromTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensData.time > toTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset deviations after site change
|
|
||||||
if (new CareportalEvent(getInjector()).isEvent5minBack(siteChanges, autosensData.time)) {
|
|
||||||
deviationsArray.clear();
|
|
||||||
pastSensitivity += "(SITECHANGE)";
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset deviations after profile switch
|
|
||||||
if (new ProfileSwitch(getInjector()).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
|
||||||
deviationsArray.clear();
|
|
||||||
pastSensitivity += "(PROFILESWITCH)";
|
|
||||||
}
|
|
||||||
|
|
||||||
double deviation = autosensData.deviation;
|
|
||||||
|
|
||||||
//set positive deviations to zero if bg < 80
|
|
||||||
if (autosensData.bg < 80 && deviation > 0)
|
|
||||||
deviation = 0;
|
|
||||||
|
|
||||||
if (autosensData.validDeviation)
|
|
||||||
if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L)
|
|
||||||
deviationsArray.add(deviation);
|
|
||||||
if (deviationsArray.size() > hoursForDetection * 60 / 5)
|
|
||||||
deviationsArray.remove(0);
|
|
||||||
|
|
||||||
|
|
||||||
pastSensitivity += autosensData.pastSensitivity;
|
|
||||||
int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time);
|
|
||||||
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
|
||||||
pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")";
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
Double[] deviations = new Double[deviationsArray.size()];
|
|
||||||
deviations = deviationsArray.toArray(deviations);
|
|
||||||
|
|
||||||
double sens = profile.getIsfMgdl();
|
|
||||||
|
|
||||||
String ratioLimit = "";
|
|
||||||
String sensResult = "";
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Records: " + index + " " + pastSensitivity);
|
|
||||||
|
|
||||||
Arrays.sort(deviations);
|
|
||||||
|
|
||||||
double percentile = IobCobCalculatorPlugin.Companion.percentile(deviations, 0.50);
|
|
||||||
double basalOff = percentile * (60.0 / 5.0) / sens;
|
|
||||||
double ratio = 1 + (basalOff / profile.getMaxDailyBasal());
|
|
||||||
|
|
||||||
if (percentile < 0) { // sensitive
|
|
||||||
sensResult = "Excess insulin sensitivity detected";
|
|
||||||
} else if (percentile > 0) { // resistant
|
|
||||||
sensResult = "Excess insulin resistance detected";
|
|
||||||
} else {
|
|
||||||
sensResult = "Sensitivity normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, sensResult);
|
|
||||||
|
|
||||||
AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit,
|
|
||||||
sensResult, deviationsArray.size());
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Sensitivity to: "
|
|
||||||
+ dateUtil.dateAndTimeString(toTime) +
|
|
||||||
" ratio: " + output.ratio
|
|
||||||
+ " mealCOB: " + current.cob);
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Sensitivity to: deviations " + Arrays.toString(deviations));
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public SensitivityType getId() {
|
|
||||||
return SensitivityType.SENSITIVITY_AAPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public JSONObject configuration() {
|
|
||||||
JSONObject c = new JSONObject();
|
|
||||||
try {
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_absorption_maxtime), getSp().getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_period), getSp().getInt(R.string.key_openapsama_autosens_period, 24));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_max), getSp().getDouble(R.string.key_openapsama_autosens_max, 1.2));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_min), getSp().getDouble(R.string.key_openapsama_autosens_min, 0.7));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void applyConfiguration(@NonNull JSONObject configuration) {
|
|
||||||
try {
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_absorption_maxtime)))
|
|
||||||
getSp().putDouble(R.string.key_absorption_maxtime, configuration.getDouble(getResourceHelper().gs(R.string.key_absorption_maxtime)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_period)))
|
|
||||||
getSp().putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_period)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_max)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_max)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_min)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_min)));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
package info.nightscout.androidaps.plugins.sensitivity
|
||||||
|
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.Constants
|
||||||
|
import info.nightscout.androidaps.MainApp
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.Profile
|
||||||
|
import info.nightscout.androidaps.db.CareportalEvent
|
||||||
|
import info.nightscout.androidaps.db.ProfileSwitch
|
||||||
|
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType
|
||||||
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.percentile
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
open class SensitivityAAPSPlugin @Inject constructor(
|
||||||
|
injector: HasAndroidInjector?,
|
||||||
|
aapsLogger: AAPSLogger?,
|
||||||
|
resourceHelper: ResourceHelper?,
|
||||||
|
sp: SP?,
|
||||||
|
private val profileFunction: ProfileFunction,
|
||||||
|
private val dateUtil: DateUtil
|
||||||
|
) : AbstractSensitivityPlugin(PluginDescription()
|
||||||
|
.mainType(PluginType.SENSITIVITY)
|
||||||
|
.pluginIcon(R.drawable.ic_generic_icon)
|
||||||
|
.pluginName(R.string.sensitivityaaps)
|
||||||
|
.shortName(R.string.sensitivity_shortname)
|
||||||
|
.preferencesId(R.xml.pref_absorption_aaps)
|
||||||
|
.description(R.string.description_sensitivity_aaps),
|
||||||
|
injector!!, aapsLogger!!, resourceHelper!!, sp!!
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult {
|
||||||
|
val autosensDataTable = plugin.getAutosensDataTable()
|
||||||
|
val age = sp.getString(R.string.key_age, "")
|
||||||
|
var defaultHours = 24
|
||||||
|
if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24
|
||||||
|
if (age == resourceHelper.gs(R.string.key_teenage)) defaultHours = 4
|
||||||
|
if (age == resourceHelper.gs(R.string.key_child)) defaultHours = 4
|
||||||
|
val hoursForDetection = sp.getInt(R.string.key_openapsama_autosens_period, defaultHours)
|
||||||
|
val profile = profileFunction.getProfile()
|
||||||
|
if (profile == null) {
|
||||||
|
aapsLogger.error("No profile")
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
if (autosensDataTable.size() < 4) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||||
|
if (current == null) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true)
|
||||||
|
val profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true)
|
||||||
|
val deviationsArray: MutableList<Double> = ArrayList()
|
||||||
|
var pastSensitivity = ""
|
||||||
|
var index = 0
|
||||||
|
while (index < autosensDataTable.size()) {
|
||||||
|
val autosensData = autosensDataTable.valueAt(index)
|
||||||
|
if (autosensData.time < fromTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (autosensData.time > toTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset deviations after site change
|
||||||
|
if (CareportalEvent(injector).isEvent5minBack(siteChanges, autosensData.time)) {
|
||||||
|
deviationsArray.clear()
|
||||||
|
pastSensitivity += "(SITECHANGE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset deviations after profile switch
|
||||||
|
if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
||||||
|
deviationsArray.clear()
|
||||||
|
pastSensitivity += "(PROFILESWITCH)"
|
||||||
|
}
|
||||||
|
var deviation = autosensData.deviation
|
||||||
|
|
||||||
|
//set positive deviations to zero if bg < 80
|
||||||
|
if (autosensData.bg < 80 && deviation > 0) deviation = 0.0
|
||||||
|
if (autosensData.validDeviation) if (autosensData.time > toTime - hoursForDetection * 60 * 60 * 1000L) deviationsArray.add(deviation)
|
||||||
|
if (deviationsArray.size > hoursForDetection * 60 / 5) deviationsArray.removeAt(0)
|
||||||
|
pastSensitivity += autosensData.pastSensitivity
|
||||||
|
val secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time)
|
||||||
|
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
||||||
|
pastSensitivity += "(" + (secondsFromMidnight / 3600.0).roundToInt() + ")"
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
val deviations = Array(deviationsArray.size) { i -> deviationsArray[i] }
|
||||||
|
val sens = profile.isfMgdl
|
||||||
|
val ratioLimit = ""
|
||||||
|
val sensResult: String
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity")
|
||||||
|
Arrays.sort(deviations)
|
||||||
|
val percentile = percentile(deviations, 0.50)
|
||||||
|
val basalOff = percentile * (60.0 / 5.0) / sens
|
||||||
|
val ratio = 1 + basalOff / profile.maxDailyBasal
|
||||||
|
sensResult = when {
|
||||||
|
percentile < 0 -> "Excess insulin sensitivity detected"
|
||||||
|
percentile > 0 -> "Excess insulin resistance detected"
|
||||||
|
else -> "Sensitivity normal"
|
||||||
|
|
||||||
|
}
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, sensResult)
|
||||||
|
val output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit,
|
||||||
|
sensResult, deviationsArray.size)
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity to: "
|
||||||
|
+ dateUtil.dateAndTimeString(toTime) +
|
||||||
|
" ratio: " + output.ratio
|
||||||
|
+ " mealCOB: " + current.cob)
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity to: deviations " + deviations.contentToString())
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
override val id: SensitivityType
|
||||||
|
get() = SensitivityType.SENSITIVITY_AAPS
|
||||||
|
|
||||||
|
override fun configuration(): JSONObject {
|
||||||
|
val c = JSONObject()
|
||||||
|
try {
|
||||||
|
c.put(resourceHelper.gs(R.string.key_absorption_maxtime), sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_period), sp.getInt(R.string.key_openapsama_autosens_period, 24))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_max), sp.getDouble(R.string.key_openapsama_autosens_max, 1.2))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_min), sp.getDouble(R.string.key_openapsama_autosens_min, 0.7))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyConfiguration(configuration: JSONObject) {
|
||||||
|
try {
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_absorption_maxtime))) sp.putDouble(R.string.key_absorption_maxtime, configuration.getDouble(resourceHelper.gs(R.string.key_absorption_maxtime)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_period))) sp.putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_period)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_max)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_min)))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,286 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.sensitivity;
|
|
||||||
|
|
||||||
import androidx.collection.LongSparseArray;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.android.HasAndroidInjector;
|
|
||||||
import info.nightscout.androidaps.Constants;
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
|
||||||
import info.nightscout.androidaps.R;
|
|
||||||
import info.nightscout.androidaps.data.Profile;
|
|
||||||
import info.nightscout.androidaps.db.CareportalEvent;
|
|
||||||
import info.nightscout.androidaps.db.ProfileSwitch;
|
|
||||||
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface;
|
|
||||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
|
||||||
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.iob.iobCobCalculator.AutosensResult;
|
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin;
|
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
|
|
||||||
import info.nightscout.androidaps.utils.DateUtil;
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mike on 19.06.2018.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class SensitivityOref1Plugin extends AbstractSensitivityPlugin {
|
|
||||||
|
|
||||||
private final ProfileFunction profileFunction;
|
|
||||||
private final DateUtil dateUtil;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SensitivityOref1Plugin(
|
|
||||||
HasAndroidInjector injector,
|
|
||||||
AAPSLogger aapsLogger,
|
|
||||||
ResourceHelper resourceHelper,
|
|
||||||
SP sp,
|
|
||||||
ProfileFunction profileFunction,
|
|
||||||
DateUtil dateUtil
|
|
||||||
) {
|
|
||||||
super(new PluginDescription()
|
|
||||||
.mainType(PluginType.SENSITIVITY)
|
|
||||||
.pluginIcon(R.drawable.ic_generic_icon)
|
|
||||||
.pluginName(R.string.sensitivityoref1)
|
|
||||||
.shortName(R.string.sensitivity_shortname)
|
|
||||||
.enableByDefault(true)
|
|
||||||
.preferencesId(R.xml.pref_absorption_oref1)
|
|
||||||
.description(R.string.description_sensitivity_oref1)
|
|
||||||
.setDefault(),
|
|
||||||
injector, aapsLogger, resourceHelper, sp
|
|
||||||
);
|
|
||||||
this.profileFunction = profileFunction;
|
|
||||||
this.dateUtil = dateUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override
|
|
||||||
public AutosensResult detectSensitivity(IobCobCalculatorInterface iobCobCalculatorPlugin, long fromTime, long toTime) {
|
|
||||||
// todo this method is called from the IobCobCalculatorPlugin, which leads to a circular
|
|
||||||
// dependency, this should be avoided
|
|
||||||
LongSparseArray<AutosensData> autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable();
|
|
||||||
|
|
||||||
Profile profile = profileFunction.getProfile();
|
|
||||||
|
|
||||||
if (profile == null) {
|
|
||||||
getAapsLogger().error("No profile");
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensDataTable == null || autosensDataTable.size() < 4) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
// the current
|
|
||||||
AutosensData current = iobCobCalculatorPlugin.getAutosensData(toTime); // this is running inside lock already
|
|
||||||
if (current == null) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CareportalEvent> siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true);
|
|
||||||
List<ProfileSwitch> profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true);
|
|
||||||
|
|
||||||
//[0] = 8 hour
|
|
||||||
//[1] = 24 hour
|
|
||||||
//Deviationshour has DeviationsArray
|
|
||||||
List<ArrayList<Double>> deviationsHour = Arrays.asList(new ArrayList<>(), new ArrayList<>());
|
|
||||||
List<String> pastSensitivityArray = Arrays.asList("", "");
|
|
||||||
List<String> sensResultArray = Arrays.asList("", "");
|
|
||||||
List<Double> ratioArray = Arrays.asList(0d, 0d);
|
|
||||||
List<Double> deviationCategory = Arrays.asList(96d, 288d);
|
|
||||||
List<String> ratioLimitArray = Arrays.asList("", "");
|
|
||||||
List<Double> hoursDetection = Arrays.asList(8d, 24d);
|
|
||||||
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
while (index < autosensDataTable.size()) {
|
|
||||||
AutosensData autosensData = autosensDataTable.valueAt(index);
|
|
||||||
|
|
||||||
if (autosensData.time < fromTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensData.time > toTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int hoursegment = 0;
|
|
||||||
//hoursegment = 0 = 8 hour
|
|
||||||
//hoursegment = 1 = 24 hour
|
|
||||||
while (hoursegment < deviationsHour.size()) {
|
|
||||||
ArrayList<Double> deviationsArray = deviationsHour.get(hoursegment);
|
|
||||||
String pastSensitivity = pastSensitivityArray.get(hoursegment);
|
|
||||||
|
|
||||||
// reset deviations after site change
|
|
||||||
if (new CareportalEvent(getInjector()).isEvent5minBack(siteChanges, autosensData.time)) {
|
|
||||||
deviationsArray.clear();
|
|
||||||
pastSensitivity += "(SITECHANGE)";
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset deviations after profile switch
|
|
||||||
if (new ProfileSwitch(getInjector()).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
|
||||||
deviationsArray.clear();
|
|
||||||
pastSensitivity += "(PROFILESWITCH)";
|
|
||||||
}
|
|
||||||
|
|
||||||
double deviation = autosensData.deviation;
|
|
||||||
|
|
||||||
//set positive deviations to zero if bg < 80
|
|
||||||
if (autosensData.bg < 80 && deviation > 0)
|
|
||||||
deviation = 0;
|
|
||||||
|
|
||||||
if (autosensData.validDeviation)
|
|
||||||
if (autosensData.time > toTime - hoursDetection.get(hoursegment) * 60 * 60 * 1000L)
|
|
||||||
deviationsArray.add(deviation);
|
|
||||||
|
|
||||||
|
|
||||||
deviationsArray.addAll(autosensData.extraDeviation);
|
|
||||||
|
|
||||||
if (deviationsArray.size() > deviationCategory.get(hoursegment)) {
|
|
||||||
deviationsArray.remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pastSensitivity += autosensData.pastSensitivity;
|
|
||||||
int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time);
|
|
||||||
|
|
||||||
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
|
||||||
pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
//Update the data back to the parent
|
|
||||||
deviationsHour.set(hoursegment, deviationsArray);
|
|
||||||
pastSensitivityArray.set(hoursegment, pastSensitivity);
|
|
||||||
hoursegment++;
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// when we have less than 8h/24 worth of deviation data, add up to 90m of zero deviations
|
|
||||||
// this dampens any large sensitivity changes detected based on too little data, without ignoring them completely
|
|
||||||
|
|
||||||
for (int i = 0; i < deviationsHour.size(); i++) {
|
|
||||||
ArrayList<Double> deviations = deviationsHour.get(i);
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Using most recent " + deviations.size() + " deviations");
|
|
||||||
if (deviations.size() < deviationCategory.get(i)) {
|
|
||||||
int pad = (int) Math.round((1 - (double) deviations.size() / deviationCategory.get(i)) * 18);
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Adding " + pad + " more zero deviations");
|
|
||||||
for (int d = 0; d < pad; d++) {
|
|
||||||
deviations.add(0d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Update the data back to the parent
|
|
||||||
deviationsHour.set(i, deviations);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int hourused = 0;
|
|
||||||
while (hourused < deviationsHour.size()) {
|
|
||||||
ArrayList deviationsArray = deviationsHour.get(hourused);
|
|
||||||
String pastSensitivity = pastSensitivityArray.get(hourused);
|
|
||||||
String sensResult = "(8 hours) ";
|
|
||||||
if (hourused == 1) sensResult = "(24 hours) ";
|
|
||||||
String ratioLimit = "";
|
|
||||||
|
|
||||||
Double[] deviations = new Double[deviationsArray.size()];
|
|
||||||
deviations = (Double[]) deviationsArray.toArray(deviations);
|
|
||||||
|
|
||||||
double sens = profile.getIsfMgdl();
|
|
||||||
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Records: " + index + " " + pastSensitivity);
|
|
||||||
|
|
||||||
Arrays.sort(deviations);
|
|
||||||
double pSensitive = IobCobCalculatorPlugin.Companion.percentile(deviations, 0.50);
|
|
||||||
double pResistant = IobCobCalculatorPlugin.Companion.percentile(deviations, 0.50);
|
|
||||||
|
|
||||||
double basalOff = 0;
|
|
||||||
|
|
||||||
if (pSensitive < 0) { // sensitive
|
|
||||||
basalOff = pSensitive * (60.0 / 5) / sens;
|
|
||||||
sensResult += "Excess insulin sensitivity detected";
|
|
||||||
} else if (pResistant > 0) { // resistant
|
|
||||||
basalOff = pResistant * (60.0 / 5) / sens;
|
|
||||||
sensResult += "Excess insulin resistance detected";
|
|
||||||
} else {
|
|
||||||
sensResult += "Sensitivity normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, sensResult);
|
|
||||||
|
|
||||||
double ratio = 1 + (basalOff / profile.getMaxDailyBasal());
|
|
||||||
|
|
||||||
//Update the data back to the parent
|
|
||||||
sensResultArray.set(hourused, sensResult);
|
|
||||||
ratioArray.set(hourused, ratio);
|
|
||||||
ratioLimitArray.set(hourused, ratioLimit);
|
|
||||||
hourused++;
|
|
||||||
}
|
|
||||||
|
|
||||||
int key = 1;
|
|
||||||
String comparison = " 8 h ratio " + ratioArray.get(0) + " vs 24h ratio " + ratioArray.get(1);
|
|
||||||
//use 24 hour ratio by default
|
|
||||||
//if the 8 hour ratio is less than the 24 hour ratio, the 8 hour ratio is used
|
|
||||||
if (ratioArray.get(0) < ratioArray.get(1)) {
|
|
||||||
key = 0;
|
|
||||||
}
|
|
||||||
//String message = hoursDetection.get(key) + " of sensitivity used";
|
|
||||||
AutosensResult output = fillResult(ratioArray.get(key), current.cob, pastSensitivityArray.get(key), ratioLimitArray.get(key),
|
|
||||||
sensResultArray.get(key) + comparison, deviationsHour.get(key).size());
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Sensitivity to: "
|
|
||||||
+ dateUtil.dateAndTimeString(toTime) +
|
|
||||||
" ratio: " + output.ratio
|
|
||||||
+ " mealCOB: " + current.cob);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public JSONObject configuration() {
|
|
||||||
JSONObject c = new JSONObject();
|
|
||||||
try {
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_min_5m_carbimpact), getSp().getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_absorption_cutoff), getSp().getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_max), getSp().getDouble(R.string.key_openapsama_autosens_max, 1.2));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_min), getSp().getDouble(R.string.key_openapsama_autosens_min, 0.7));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void applyConfiguration(@NonNull JSONObject configuration) {
|
|
||||||
try {
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_min_5m_carbimpact)))
|
|
||||||
getSp().putDouble(R.string.key_openapsama_min_5m_carbimpact, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_min_5m_carbimpact)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_absorption_cutoff)))
|
|
||||||
getSp().putDouble(R.string.key_absorption_cutoff, configuration.getDouble(getResourceHelper().gs(R.string.key_absorption_cutoff)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_max)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_max)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_min)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_min)));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public SensitivityType getId() {
|
|
||||||
return SensitivityType.SENSITIVITY_OREF1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
package info.nightscout.androidaps.plugins.sensitivity
|
||||||
|
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.Constants
|
||||||
|
import info.nightscout.androidaps.MainApp
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.Profile
|
||||||
|
import info.nightscout.androidaps.db.CareportalEvent
|
||||||
|
import info.nightscout.androidaps.db.ProfileSwitch
|
||||||
|
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType
|
||||||
|
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.iob.iobCobCalculator.AutosensResult
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin.Companion.percentile
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
open class SensitivityOref1Plugin @Inject constructor(
|
||||||
|
injector: HasAndroidInjector?,
|
||||||
|
aapsLogger: AAPSLogger?,
|
||||||
|
resourceHelper: ResourceHelper?,
|
||||||
|
sp: SP?,
|
||||||
|
private val profileFunction: ProfileFunction,
|
||||||
|
private val dateUtil: DateUtil
|
||||||
|
) : AbstractSensitivityPlugin(PluginDescription()
|
||||||
|
.mainType(PluginType.SENSITIVITY)
|
||||||
|
.pluginIcon(R.drawable.ic_generic_icon)
|
||||||
|
.pluginName(R.string.sensitivityoref1)
|
||||||
|
.shortName(R.string.sensitivity_shortname)
|
||||||
|
.enableByDefault(true)
|
||||||
|
.preferencesId(R.xml.pref_absorption_oref1)
|
||||||
|
.description(R.string.description_sensitivity_oref1)
|
||||||
|
.setDefault(),
|
||||||
|
injector!!, aapsLogger!!, resourceHelper!!, sp!!
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult {
|
||||||
|
// todo this method is called from the IobCobCalculatorPlugin, which leads to a circular
|
||||||
|
// dependency, this should be avoided
|
||||||
|
val autosensDataTable = plugin.getAutosensDataTable()
|
||||||
|
val profile = profileFunction.getProfile()
|
||||||
|
if (profile == null) {
|
||||||
|
aapsLogger.error("No profile")
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
if (autosensDataTable.size() < 4) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
// the current
|
||||||
|
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||||
|
if (current == null) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true)
|
||||||
|
val profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true)
|
||||||
|
|
||||||
|
//[0] = 8 hour
|
||||||
|
//[1] = 24 hour
|
||||||
|
//deviationsHour has DeviationsArray
|
||||||
|
val deviationsHour = mutableListOf(ArrayList<Double>(), ArrayList<Double>())
|
||||||
|
val pastSensitivityArray = mutableListOf("", "")
|
||||||
|
val sensResultArray = mutableListOf("", "")
|
||||||
|
val ratioArray = mutableListOf(0.0, 0.0)
|
||||||
|
val deviationCategory = listOf(96.0, 288.0)
|
||||||
|
val ratioLimitArray = mutableListOf("", "")
|
||||||
|
val hoursDetection = listOf(8.0, 24.0)
|
||||||
|
var index = 0
|
||||||
|
while (index < autosensDataTable.size()) {
|
||||||
|
val autosensData = autosensDataTable.valueAt(index)
|
||||||
|
if (autosensData.time < fromTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (autosensData.time > toTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var hourSegment = 0
|
||||||
|
//hourSegment = 0 = 8 hour
|
||||||
|
//hourSegment = 1 = 24 hour
|
||||||
|
while (hourSegment < deviationsHour.size) {
|
||||||
|
val deviationsArray = deviationsHour[hourSegment]
|
||||||
|
var pastSensitivity = pastSensitivityArray[hourSegment]
|
||||||
|
|
||||||
|
// reset deviations after site change
|
||||||
|
if (CareportalEvent(injector).isEvent5minBack(siteChanges, autosensData.time)) {
|
||||||
|
deviationsArray.clear()
|
||||||
|
pastSensitivity += "(SITECHANGE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset deviations after profile switch
|
||||||
|
if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
||||||
|
deviationsArray.clear()
|
||||||
|
pastSensitivity += "(PROFILESWITCH)"
|
||||||
|
}
|
||||||
|
var deviation = autosensData.deviation
|
||||||
|
|
||||||
|
//set positive deviations to zero if bg < 80
|
||||||
|
if (autosensData.bg < 80 && deviation > 0) deviation = 0.0
|
||||||
|
if (autosensData.validDeviation) if (autosensData.time > toTime - hoursDetection[hourSegment] * 60 * 60 * 1000L) deviationsArray.add(deviation)
|
||||||
|
deviationsArray.addAll(autosensData.extraDeviation)
|
||||||
|
if (deviationsArray.size > deviationCategory[hourSegment]) {
|
||||||
|
deviationsArray.removeAt(0)
|
||||||
|
}
|
||||||
|
pastSensitivity += autosensData.pastSensitivity
|
||||||
|
val secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time)
|
||||||
|
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
||||||
|
pastSensitivity += "(" + (secondsFromMidnight / 3600.0).roundToInt() + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update the data back to the parent
|
||||||
|
deviationsHour[hourSegment] = deviationsArray
|
||||||
|
pastSensitivityArray[hourSegment] = pastSensitivity
|
||||||
|
hourSegment++
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
|
// when we have less than 8h/24 worth of deviation data, add up to 90m of zero deviations
|
||||||
|
// this dampens any large sensitivity changes detected based on too little data, without ignoring them completely
|
||||||
|
for (i in deviationsHour.indices) {
|
||||||
|
val deviations = deviationsHour[i]
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Using most recent " + deviations.size + " deviations")
|
||||||
|
if (deviations.size < deviationCategory[i]) {
|
||||||
|
val pad = ((1 - deviations.size.toDouble() / deviationCategory[i]) * 18).roundToInt()
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Adding $pad more zero deviations")
|
||||||
|
for (d in 0 until pad) {
|
||||||
|
deviations.add(0.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Update the data back to the parent
|
||||||
|
deviationsHour[i] = deviations
|
||||||
|
}
|
||||||
|
var hourUsed = 0
|
||||||
|
while (hourUsed < deviationsHour.size) {
|
||||||
|
val deviationsArray: ArrayList<Double> = deviationsHour[hourUsed]
|
||||||
|
val pastSensitivity = pastSensitivityArray[hourUsed]
|
||||||
|
var sensResult = "(8 hours) "
|
||||||
|
if (hourUsed == 1) sensResult = "(24 hours) "
|
||||||
|
val ratioLimit = ""
|
||||||
|
val deviations: Array<Double> = Array(deviationsArray.size) { i -> deviationsArray[i] }
|
||||||
|
val sens = profile.isfMgdl
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity")
|
||||||
|
Arrays.sort(deviations)
|
||||||
|
val pSensitive = percentile(deviations, 0.50)
|
||||||
|
val pResistant = percentile(deviations, 0.50)
|
||||||
|
var basalOff = 0.0
|
||||||
|
when {
|
||||||
|
pSensitive < 0 -> { // sensitive
|
||||||
|
basalOff = pSensitive * (60.0 / 5) / sens
|
||||||
|
sensResult += "Excess insulin sensitivity detected"
|
||||||
|
}
|
||||||
|
|
||||||
|
pResistant > 0 -> { // resistant
|
||||||
|
basalOff = pResistant * (60.0 / 5) / sens
|
||||||
|
sensResult += "Excess insulin resistance detected"
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> sensResult += "Sensitivity normal"
|
||||||
|
}
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, sensResult)
|
||||||
|
val ratio = 1 + basalOff / profile.maxDailyBasal
|
||||||
|
|
||||||
|
//Update the data back to the parent
|
||||||
|
sensResultArray[hourUsed] = sensResult
|
||||||
|
ratioArray[hourUsed] = ratio
|
||||||
|
ratioLimitArray[hourUsed] = ratioLimit
|
||||||
|
hourUsed++
|
||||||
|
}
|
||||||
|
var key = 1
|
||||||
|
val comparison = " 8 h ratio " + ratioArray[0] + " vs 24h ratio " + ratioArray[1]
|
||||||
|
//use 24 hour ratio by default
|
||||||
|
//if the 8 hour ratio is less than the 24 hour ratio, the 8 hour ratio is used
|
||||||
|
if (ratioArray[0] < ratioArray[1]) {
|
||||||
|
key = 0
|
||||||
|
}
|
||||||
|
//String message = hoursDetection.get(key) + " of sensitivity used";
|
||||||
|
val output = fillResult(ratioArray[key], current.cob, pastSensitivityArray[key], ratioLimitArray[key], sensResultArray[key] + comparison, deviationsHour[key].size)
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity to: "
|
||||||
|
+ dateUtil.dateAndTimeString(toTime) +
|
||||||
|
" ratio: " + output.ratio
|
||||||
|
+ " mealCOB: " + current.cob)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configuration(): JSONObject {
|
||||||
|
val c = JSONObject()
|
||||||
|
try {
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_min_5m_carbimpact), sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, SMBDefaults.min_5m_carbimpact))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_absorption_cutoff), sp.getDouble(R.string.key_absorption_cutoff, Constants.DEFAULT_MAX_ABSORPTION_TIME))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_max), sp.getDouble(R.string.key_openapsama_autosens_max, 1.2))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_min), sp.getDouble(R.string.key_openapsama_autosens_min, 0.7))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyConfiguration(configuration: JSONObject) {
|
||||||
|
try {
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_min_5m_carbimpact))) sp.putDouble(R.string.key_openapsama_min_5m_carbimpact, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_min_5m_carbimpact)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_absorption_cutoff))) sp.putDouble(R.string.key_absorption_cutoff, configuration.getDouble(resourceHelper.gs(R.string.key_absorption_cutoff)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_max)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_min)))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val id: SensitivityType
|
||||||
|
get() = SensitivityType.SENSITIVITY_OREF1
|
||||||
|
}
|
|
@ -1,240 +0,0 @@
|
||||||
package info.nightscout.androidaps.plugins.sensitivity;
|
|
||||||
|
|
||||||
import androidx.collection.LongSparseArray;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import dagger.android.HasAndroidInjector;
|
|
||||||
import info.nightscout.androidaps.Constants;
|
|
||||||
import info.nightscout.androidaps.MainApp;
|
|
||||||
import info.nightscout.androidaps.R;
|
|
||||||
import info.nightscout.androidaps.data.Profile;
|
|
||||||
import info.nightscout.androidaps.db.CareportalEvent;
|
|
||||||
import info.nightscout.androidaps.db.ProfileSwitch;
|
|
||||||
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface;
|
|
||||||
import info.nightscout.androidaps.interfaces.PluginDescription;
|
|
||||||
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.iob.iobCobCalculator.AutosensResult;
|
|
||||||
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.data.AutosensData;
|
|
||||||
import info.nightscout.androidaps.utils.DateUtil;
|
|
||||||
import info.nightscout.androidaps.utils.resources.ResourceHelper;
|
|
||||||
import info.nightscout.androidaps.utils.sharedPreferences.SP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mike on 24.06.2017.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public class SensitivityWeightedAveragePlugin extends AbstractSensitivityPlugin {
|
|
||||||
|
|
||||||
private final SP sp;
|
|
||||||
private final DateUtil dateUtil;
|
|
||||||
private final ProfileFunction profileFunction;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SensitivityWeightedAveragePlugin(
|
|
||||||
HasAndroidInjector injector,
|
|
||||||
AAPSLogger aapsLogger,
|
|
||||||
ResourceHelper resourceHelper,
|
|
||||||
SP sp,
|
|
||||||
ProfileFunction profileFunction,
|
|
||||||
DateUtil dateUtil
|
|
||||||
) {
|
|
||||||
super(new PluginDescription()
|
|
||||||
.mainType(PluginType.SENSITIVITY)
|
|
||||||
.pluginIcon(R.drawable.ic_generic_icon)
|
|
||||||
.pluginName(R.string.sensitivityweightedaverage)
|
|
||||||
.shortName(R.string.sensitivity_shortname)
|
|
||||||
.preferencesId(R.xml.pref_absorption_aaps)
|
|
||||||
.description(R.string.description_sensitivity_weighted_average),
|
|
||||||
injector, aapsLogger, resourceHelper, sp
|
|
||||||
);
|
|
||||||
this.sp = sp;
|
|
||||||
this.dateUtil = dateUtil;
|
|
||||||
this.profileFunction = profileFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AutosensResult detectSensitivity(IobCobCalculatorInterface iobCobCalculatorPlugin, long fromTime, long toTime) {
|
|
||||||
LongSparseArray<AutosensData> autosensDataTable = iobCobCalculatorPlugin.getAutosensDataTable();
|
|
||||||
|
|
||||||
String age = sp.getString(R.string.key_age, "");
|
|
||||||
int defaultHours = 24;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_adult))) defaultHours = 24;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_teenage))) defaultHours = 4;
|
|
||||||
if (age.equals(getResourceHelper().gs(R.string.key_child))) defaultHours = 4;
|
|
||||||
int hoursForDetection = sp.getInt(R.string.key_openapsama_autosens_period, defaultHours);
|
|
||||||
|
|
||||||
if (autosensDataTable == null || autosensDataTable.size() < 4) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
AutosensData current = iobCobCalculatorPlugin.getAutosensData(toTime); // this is running inside lock already
|
|
||||||
if (current == null) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + iobCobCalculatorPlugin.lastDataTime());
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Profile profile = profileFunction.getProfile();
|
|
||||||
if (profile == null) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "No profile available");
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<CareportalEvent> siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true);
|
|
||||||
List<ProfileSwitch> profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true);
|
|
||||||
|
|
||||||
String pastSensitivity = "";
|
|
||||||
int index = 0;
|
|
||||||
LongSparseArray<Double> data = new LongSparseArray<>();
|
|
||||||
|
|
||||||
while (index < autosensDataTable.size()) {
|
|
||||||
AutosensData autosensData = autosensDataTable.valueAt(index);
|
|
||||||
|
|
||||||
if (autosensData.time < fromTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensData.time > toTime) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (autosensData.time < toTime - hoursForDetection * 60 * 60 * 1000L) {
|
|
||||||
index++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset deviations after site change
|
|
||||||
if (new CareportalEvent(getInjector()).isEvent5minBack(siteChanges, autosensData.time)) {
|
|
||||||
data.clear();
|
|
||||||
pastSensitivity += "(SITECHANGE)";
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset deviations after profile switch
|
|
||||||
if (new ProfileSwitch(getInjector()).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
|
||||||
data.clear();
|
|
||||||
pastSensitivity += "(PROFILESWITCH)";
|
|
||||||
}
|
|
||||||
|
|
||||||
double deviation = autosensData.deviation;
|
|
||||||
|
|
||||||
//set positive deviations to zero if bg < 80
|
|
||||||
if (autosensData.bg < 80 && deviation > 0)
|
|
||||||
deviation = 0;
|
|
||||||
|
|
||||||
//data.append(autosensData.time);
|
|
||||||
long reverseWeight = (toTime - autosensData.time) / (5 * 60 * 1000L);
|
|
||||||
if (autosensData.validDeviation)
|
|
||||||
data.append(reverseWeight, deviation);
|
|
||||||
//weights += reverseWeight;
|
|
||||||
//weightedsum += reverseWeight * (autosensData.validDeviation ? autosensData.deviation : 0d);
|
|
||||||
|
|
||||||
|
|
||||||
pastSensitivity += autosensData.pastSensitivity;
|
|
||||||
int secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time);
|
|
||||||
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
|
||||||
pastSensitivity += "(" + Math.round(secondsFromMidnight / 3600d) + ")";
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.size() == 0) {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Data size: " + data.size() + " fromTime: " + dateUtil.dateAndTimeString(fromTime) + " toTime: " + dateUtil.dateAndTimeString(toTime));
|
|
||||||
return new AutosensResult();
|
|
||||||
} else {
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Data size: " + data.size() + " fromTime: " + dateUtil.dateAndTimeString(fromTime) + " toTime: " + dateUtil.dateAndTimeString(toTime));
|
|
||||||
}
|
|
||||||
|
|
||||||
double weightedsum = 0;
|
|
||||||
double weights = 0;
|
|
||||||
|
|
||||||
long hightestWeight = data.keyAt(data.size() - 1);
|
|
||||||
for (int i = 0; i < data.size(); i++) {
|
|
||||||
long reversedWeigth = data.keyAt(i);
|
|
||||||
double value = data.valueAt(i);
|
|
||||||
double weight = (hightestWeight - reversedWeigth) / 2.0;
|
|
||||||
weights += weight;
|
|
||||||
weightedsum += weight * value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (weights == 0) {
|
|
||||||
return new AutosensResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
double sens = profile.getIsfMgdl();
|
|
||||||
|
|
||||||
String ratioLimit = "";
|
|
||||||
String sensResult;
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Records: " + index + " " + pastSensitivity);
|
|
||||||
|
|
||||||
double average = weightedsum / weights;
|
|
||||||
double basalOff = average * (60 / 5.0) / sens;
|
|
||||||
double ratio = 1 + (basalOff / profile.getMaxDailyBasal());
|
|
||||||
|
|
||||||
if (average < 0) { // sensitive
|
|
||||||
sensResult = "Excess insulin sensitivity detected";
|
|
||||||
} else if (average > 0) { // resistant
|
|
||||||
sensResult = "Excess insulin resistance detected";
|
|
||||||
} else {
|
|
||||||
sensResult = "Sensitivity normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, sensResult);
|
|
||||||
|
|
||||||
AutosensResult output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit,
|
|
||||||
sensResult, data.size());
|
|
||||||
|
|
||||||
getAapsLogger().debug(LTag.AUTOSENS, "Sensitivity to: "
|
|
||||||
+ dateUtil.dateAndTimeString(toTime) +
|
|
||||||
" ratio: " + output.ratio
|
|
||||||
+ " mealCOB: " + current.cob);
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public SensitivityType getId() {
|
|
||||||
return SensitivityType.SENSITIVITY_WEIGHTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override public JSONObject configuration() {
|
|
||||||
JSONObject c = new JSONObject();
|
|
||||||
try {
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_absorption_maxtime), getSp().getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_period), getSp().getInt(R.string.key_openapsama_autosens_period, 24));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_max), getSp().getDouble(R.string.key_openapsama_autosens_max, 1.2));
|
|
||||||
c.put(getResourceHelper().gs(R.string.key_openapsama_autosens_min), getSp().getDouble(R.string.key_openapsama_autosens_min, 0.7));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void applyConfiguration(@NonNull JSONObject configuration) {
|
|
||||||
try {
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_absorption_maxtime)))
|
|
||||||
getSp().putDouble(R.string.key_absorption_maxtime, configuration.getDouble(getResourceHelper().gs(R.string.key_absorption_maxtime)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_period)))
|
|
||||||
getSp().putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_period)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_max)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_max)));
|
|
||||||
if (configuration.has(getResourceHelper().gs(R.string.key_openapsama_autosens_min)))
|
|
||||||
getSp().getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(getResourceHelper().gs(R.string.key_openapsama_autosens_min)));
|
|
||||||
} catch (JSONException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
package info.nightscout.androidaps.plugins.sensitivity
|
||||||
|
|
||||||
|
import androidx.collection.LongSparseArray
|
||||||
|
import dagger.android.HasAndroidInjector
|
||||||
|
import info.nightscout.androidaps.Constants
|
||||||
|
import info.nightscout.androidaps.MainApp
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import info.nightscout.androidaps.data.Profile
|
||||||
|
import info.nightscout.androidaps.db.CareportalEvent
|
||||||
|
import info.nightscout.androidaps.db.ProfileSwitch
|
||||||
|
import info.nightscout.androidaps.interfaces.IobCobCalculatorInterface
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginDescription
|
||||||
|
import info.nightscout.androidaps.interfaces.PluginType
|
||||||
|
import info.nightscout.androidaps.interfaces.ProfileFunction
|
||||||
|
import info.nightscout.androidaps.interfaces.SensitivityInterface.SensitivityType
|
||||||
|
import info.nightscout.androidaps.logging.AAPSLogger
|
||||||
|
import info.nightscout.androidaps.logging.LTag
|
||||||
|
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
|
||||||
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
|
import info.nightscout.androidaps.utils.resources.ResourceHelper
|
||||||
|
import info.nightscout.androidaps.utils.sharedPreferences.SP
|
||||||
|
import org.json.JSONException
|
||||||
|
import org.json.JSONObject
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
open class SensitivityWeightedAveragePlugin @Inject constructor(
|
||||||
|
injector: HasAndroidInjector,
|
||||||
|
aapsLogger: AAPSLogger,
|
||||||
|
resourceHelper: ResourceHelper,
|
||||||
|
sp: SP,
|
||||||
|
private val profileFunction: ProfileFunction,
|
||||||
|
private val dateUtil: DateUtil
|
||||||
|
) : AbstractSensitivityPlugin(PluginDescription()
|
||||||
|
.mainType(PluginType.SENSITIVITY)
|
||||||
|
.pluginIcon(R.drawable.ic_generic_icon)
|
||||||
|
.pluginName(R.string.sensitivityweightedaverage)
|
||||||
|
.shortName(R.string.sensitivity_shortname)
|
||||||
|
.preferencesId(R.xml.pref_absorption_aaps)
|
||||||
|
.description(R.string.description_sensitivity_weighted_average),
|
||||||
|
injector, aapsLogger, resourceHelper, sp
|
||||||
|
) {
|
||||||
|
|
||||||
|
override fun detectSensitivity(plugin: IobCobCalculatorInterface, fromTime: Long, toTime: Long): AutosensResult {
|
||||||
|
val autosensDataTable = plugin.getAutosensDataTable()
|
||||||
|
val age = sp.getString(R.string.key_age, "")
|
||||||
|
var defaultHours = 24
|
||||||
|
if (age == resourceHelper.gs(R.string.key_adult)) defaultHours = 24
|
||||||
|
if (age == resourceHelper.gs(R.string.key_teenage)) defaultHours = 4
|
||||||
|
if (age == resourceHelper.gs(R.string.key_child)) defaultHours = 4
|
||||||
|
val hoursForDetection = sp.getInt(R.string.key_openapsama_autosens_period, defaultHours)
|
||||||
|
if (autosensDataTable.size() < 4) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. lastDataTime=" + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val current = plugin.getAutosensData(toTime) // this is running inside lock already
|
||||||
|
if (current == null) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No autosens data available. toTime: " + dateUtil.dateAndTimeString(toTime) + " lastDataTime: " + plugin.lastDataTime())
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val profile = profileFunction.getProfile()
|
||||||
|
if (profile == null) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "No profile available")
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val siteChanges = MainApp.getDbHelper().getCareportalEventsFromTime(fromTime, CareportalEvent.SITECHANGE, true)
|
||||||
|
val profileSwitches = MainApp.getDbHelper().getProfileSwitchEventsFromTime(fromTime, true)
|
||||||
|
var pastSensitivity = ""
|
||||||
|
var index = 0
|
||||||
|
val data = LongSparseArray<Double>()
|
||||||
|
while (index < autosensDataTable.size()) {
|
||||||
|
val autosensData = autosensDataTable.valueAt(index)
|
||||||
|
if (autosensData.time < fromTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (autosensData.time > toTime) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (autosensData.time < toTime - hoursForDetection * 60 * 60 * 1000L) {
|
||||||
|
index++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset deviations after site change
|
||||||
|
if (CareportalEvent(injector).isEvent5minBack(siteChanges, autosensData.time)) {
|
||||||
|
data.clear()
|
||||||
|
pastSensitivity += "(SITECHANGE)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset deviations after profile switch
|
||||||
|
if (ProfileSwitch(injector).isEvent5minBack(profileSwitches, autosensData.time, true)) {
|
||||||
|
data.clear()
|
||||||
|
pastSensitivity += "(PROFILESWITCH)"
|
||||||
|
}
|
||||||
|
var deviation = autosensData.deviation
|
||||||
|
|
||||||
|
//set positive deviations to zero if bg < 80
|
||||||
|
if (autosensData.bg < 80 && deviation > 0) deviation = 0.0
|
||||||
|
|
||||||
|
//data.append(autosensData.time);
|
||||||
|
val reverseWeight = (toTime - autosensData.time) / (5 * 60 * 1000L)
|
||||||
|
if (autosensData.validDeviation) data.append(reverseWeight, deviation)
|
||||||
|
pastSensitivity += autosensData.pastSensitivity
|
||||||
|
val secondsFromMidnight = Profile.secondsFromMidnight(autosensData.time)
|
||||||
|
if (secondsFromMidnight % 3600 < 2.5 * 60 || secondsFromMidnight % 3600 > 57.5 * 60) {
|
||||||
|
pastSensitivity += "(" + (secondsFromMidnight / 3600.0).roundToInt() + ")"
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
if (data.size() == 0) {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Data size: " + data.size() + " fromTime: " + dateUtil.dateAndTimeString(fromTime) + " toTime: " + dateUtil.dateAndTimeString(toTime))
|
||||||
|
return AutosensResult()
|
||||||
|
} else {
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Data size: " + data.size() + " fromTime: " + dateUtil.dateAndTimeString(fromTime) + " toTime: " + dateUtil.dateAndTimeString(toTime))
|
||||||
|
}
|
||||||
|
var weightedSum = 0.0
|
||||||
|
var weights = 0.0
|
||||||
|
val highestWeight = data.keyAt(data.size() - 1)
|
||||||
|
for (i in 0 until data.size()) {
|
||||||
|
val reversedWeight = data.keyAt(i)
|
||||||
|
val value = data.valueAt(i)
|
||||||
|
val weight = (highestWeight - reversedWeight) / 2.0
|
||||||
|
weights += weight
|
||||||
|
weightedSum += weight * value
|
||||||
|
}
|
||||||
|
if (weights == 0.0) {
|
||||||
|
return AutosensResult()
|
||||||
|
}
|
||||||
|
val sens = profile.isfMgdl
|
||||||
|
val ratioLimit = ""
|
||||||
|
val sensResult: String
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity")
|
||||||
|
val average = weightedSum / weights
|
||||||
|
val basalOff = average * (60 / 5.0) / sens
|
||||||
|
val ratio = 1 + basalOff / profile.maxDailyBasal
|
||||||
|
sensResult = when {
|
||||||
|
average < 0 -> "Excess insulin sensitivity detected"
|
||||||
|
average > 0 -> "Excess insulin resistance detected"
|
||||||
|
else -> "Sensitivity normal"
|
||||||
|
}
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, sensResult)
|
||||||
|
val output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit,
|
||||||
|
sensResult, data.size())
|
||||||
|
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity to: "
|
||||||
|
+ dateUtil.dateAndTimeString(toTime) +
|
||||||
|
" ratio: " + output.ratio
|
||||||
|
+ " mealCOB: " + current.cob)
|
||||||
|
return output
|
||||||
|
}
|
||||||
|
|
||||||
|
override val id: SensitivityType
|
||||||
|
get() = SensitivityType.SENSITIVITY_WEIGHTED
|
||||||
|
|
||||||
|
override fun configuration(): JSONObject {
|
||||||
|
val c = JSONObject()
|
||||||
|
try {
|
||||||
|
c.put(resourceHelper.gs(R.string.key_absorption_maxtime), sp.getDouble(R.string.key_absorption_maxtime, Constants.DEFAULT_MAX_ABSORPTION_TIME))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_period), sp.getInt(R.string.key_openapsama_autosens_period, 24))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_max), sp.getDouble(R.string.key_openapsama_autosens_max, 1.2))
|
||||||
|
c.put(resourceHelper.gs(R.string.key_openapsama_autosens_min), sp.getDouble(R.string.key_openapsama_autosens_min, 0.7))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyConfiguration(configuration: JSONObject) {
|
||||||
|
try {
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_absorption_maxtime))) sp.putDouble(R.string.key_absorption_maxtime, configuration.getDouble(resourceHelper.gs(R.string.key_absorption_maxtime)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_period))) sp.putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_period)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_max)))
|
||||||
|
if (configuration.has(resourceHelper.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(resourceHelper.gs(R.string.key_openapsama_autosens_min)))
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue