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