Sensitivity plugins -> kt

This commit is contained in:
Milos Kozak 2021-02-25 15:32:09 +01:00
parent 951b35eecb
commit e6f229b15b
6 changed files with 570 additions and 746 deletions

View file

@ -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();
}
}
}

View file

@ -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()
}
}
}

View file

@ -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;
}
}

View file

@ -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
}

View file

@ -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();
}
}
}

View file

@ -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()
}
}
}