AutotunePlugin -> plugins module

This commit is contained in:
Milos Kozak 2022-11-04 20:43:11 +01:00
parent efcae9f073
commit f0f5a89fe6
37 changed files with 403 additions and 348 deletions

View file

@ -37,13 +37,11 @@ import info.nightscout.androidaps.plugins.aps.openAPSSMBDynamicISF.OpenAPSSMBDyn
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.constraints.safety.SafetyPlugin
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
@ -67,7 +65,9 @@ import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionTyp
import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.CUSTOM_PIN
import info.nightscout.androidaps.utils.protection.ProtectionCheck.ProtectionType.NONE
import info.nightscout.automation.AutomationPlugin
import info.nightscout.plugins.general.autotune.AutotunePlugin
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.plugins.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP
@ -335,9 +335,9 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
p.initialExpandedChildrenCount = Int.MAX_VALUE
}
} else {
visible = visible || p.key?.contains(filter, true) == true
visible = visible || p.title?.contains(filter, true) == true
visible = visible || p.summary?.contains(filter, true) == true
visible = visible || p.key?.contains(filter, true) == true
visible = visible || p.title?.contains(filter, true) == true
visible = visible || p.summary?.contains(filter, true) == true
}
p.isVisible = visible
@ -405,7 +405,7 @@ class MyPreferenceFragment : PreferenceFragmentCompat(), OnSharedPreferenceChang
} else {
if (pref.key.contains("pin")) {
pref.summary = rh.gs(R.string.pin_not_set)
}else {
} else {
pref.summary = rh.gs(R.string.password_not_set)
}
}

View file

@ -11,7 +11,6 @@ import info.nightscout.androidaps.dana.di.DanaModule
import info.nightscout.androidaps.danar.di.DanaRModule
import info.nightscout.androidaps.danars.di.DanaRSModule
import info.nightscout.androidaps.database.DatabaseModule
import info.nightscout.androidaps.dependencyInjection.AutotuneModule
import info.nightscout.androidaps.diaconn.di.DiaconnG8Module
import info.nightscout.androidaps.insight.di.InsightDatabaseModule
import info.nightscout.androidaps.insight.di.InsightModule
@ -40,7 +39,6 @@ import javax.inject.Singleton
FragmentsModule::class,
ReceiversModule::class,
ServicesModule::class,
AutotuneModule::class,
ObjectivesModule::class,
WizardModule::class,
APSModule::class,

View file

@ -42,7 +42,6 @@ import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderPlugin
import info.nightscout.androidaps.plugins.configBuilder.PluginStore
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctionImpl
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
import info.nightscout.androidaps.plugins.general.maintenance.ImportExportPrefsImpl
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import info.nightscout.androidaps.plugins.general.nsclient.DataSyncSelectorImplementation
@ -71,6 +70,7 @@ import info.nightscout.implementation.queue.CommandQueueImplementation
import info.nightscout.implementation.stats.DexcomTirCalculatorImpl
import info.nightscout.implementation.stats.TddCalculatorImpl
import info.nightscout.implementation.stats.TirCalculatorImpl
import info.nightscout.plugins.general.autotune.AutotunePlugin
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP

View file

@ -27,17 +27,16 @@ import info.nightscout.androidaps.plugins.configBuilder.ConfigBuilderFragment
import info.nightscout.androidaps.plugins.constraints.objectives.ObjectivesFragment
import info.nightscout.androidaps.plugins.constraints.objectives.activities.ObjectivesExamDialog
import info.nightscout.androidaps.plugins.general.actions.ActionsFragment
import info.nightscout.androidaps.plugins.general.autotune.AutotuneFragment
import info.nightscout.androidaps.plugins.general.maintenance.MaintenanceFragment
import info.nightscout.androidaps.plugins.general.nsclient.NSClientFragment
import info.nightscout.androidaps.plugins.general.overview.OverviewFragment
import info.nightscout.androidaps.plugins.general.overview.dialogs.EditQuickWizardDialog
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolFragment
import info.nightscout.androidaps.plugins.general.wear.WearFragment
import info.nightscout.plugins.profile.ProfileFragment
import info.nightscout.androidaps.plugins.pump.virtual.VirtualPumpFragment
import info.nightscout.androidaps.plugins.source.BGSourceFragment
import info.nightscout.androidaps.utils.protection.PasswordCheck
import info.nightscout.plugins.general.autotune.AutotuneFragment
@Module
@Suppress("unused")
@ -48,10 +47,7 @@ abstract class FragmentsModule {
@ContributesAndroidInjector abstract fun contributesActionsFragment(): ActionsFragment
@ContributesAndroidInjector abstract fun contributesAutotuneFragment(): AutotuneFragment
@ContributesAndroidInjector abstract fun contributesBGSourceFragment(): BGSourceFragment
@ContributesAndroidInjector abstract fun contributesConfigBuilderFragment(): ConfigBuilderFragment
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): ProfileFragment
@ContributesAndroidInjector abstract fun contributesObjectivesFragment(): ObjectivesFragment
@ContributesAndroidInjector abstract fun contributesOpenAPSFragment(): OpenAPSFragment
@ContributesAndroidInjector abstract fun contributesOverviewFragment(): OverviewFragment

View file

@ -24,18 +24,14 @@ import info.nightscout.androidaps.plugins.constraints.signatureVerifier.Signatur
import info.nightscout.androidaps.plugins.constraints.storage.StorageConstraintPlugin
import info.nightscout.androidaps.plugins.constraints.versionChecker.VersionCheckerPlugin
import info.nightscout.androidaps.plugins.general.actions.ActionsPlugin
import info.nightscout.androidaps.plugins.general.autotune.AutotunePlugin
import info.nightscout.androidaps.plugins.general.dataBroadcaster.DataBroadcastPlugin
import info.nightscout.plugins.general.food.FoodPlugin
import info.nightscout.androidaps.plugins.general.maintenance.MaintenancePlugin
import info.nightscout.androidaps.plugins.general.nsclient.NSClientPlugin
import info.nightscout.androidaps.plugins.general.overview.OverviewPlugin
import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin
import info.nightscout.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.androidaps.plugins.general.tidepool.TidepoolPlugin
import info.nightscout.androidaps.plugins.general.wear.WearPlugin
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.androidaps.plugins.pump.combo.ComboPlugin
import info.nightscout.androidaps.plugins.pump.eopatch.EopatchPumpPlugin
import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin
@ -58,12 +54,16 @@ import info.nightscout.androidaps.plugins.source.RandomBgPlugin
import info.nightscout.androidaps.plugins.source.TomatoPlugin
import info.nightscout.androidaps.plugins.source.XdripPlugin
import info.nightscout.automation.AutomationPlugin
import info.nightscout.plugins.general.autotune.AutotunePlugin
import info.nightscout.plugins.general.food.FoodPlugin
import info.nightscout.plugins.general.smsCommunicator.SmsCommunicatorPlugin
import info.nightscout.plugins.general.themes.ThemeSwitcherPlugin
import info.nightscout.plugins.general.xdripStatusline.StatusLinePlugin
import info.nightscout.plugins.insulin.InsulinLyumjevPlugin
import info.nightscout.plugins.insulin.InsulinOrefFreePeakPlugin
import info.nightscout.plugins.insulin.InsulinOrefRapidActingPlugin
import info.nightscout.plugins.insulin.InsulinOrefUltraRapidActingPlugin
import info.nightscout.plugins.profile.ProfilePlugin
import javax.inject.Qualifier
@Suppress("unused")

View file

@ -7,17 +7,23 @@ import androidx.core.content.FileProvider
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.R
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.plugins.general.maintenance.LoggerUtils
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import java.io.*
import java.util.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.util.Arrays
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
import javax.inject.Inject

View file

@ -51,7 +51,6 @@ import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.min
@ -450,23 +449,6 @@ class IobCobCalculatorPlugin @Inject constructor(
return array
}
companion object {
// From https://gist.github.com/IceCreamYou/6ffa1b18c4c8f6aeaad2
// Returns the value at a given percentile in a sorted numeric array.
// "Linear interpolation between closest ranks" method
fun percentile(arr: Array<Double>, p: Double): Double {
if (arr.isEmpty()) return 0.0
if (p <= 0) return arr[0]
if (p >= 1) return arr[arr.size - 1]
val index = arr.size * p
val lower = floor(index)
val upper = lower + 1
val weight = index % 1
return if (upper >= arr.size) arr[lower.toInt()] else arr[lower.toInt()] * (1 - weight) + arr[upper.toInt()] * weight
}
}
/**
* Time range to the past for IOB calculation
* @return milliseconds

View file

@ -12,18 +12,18 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.plugins.utils.Percentile
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import java.util.Arrays
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.roundToInt
@ -116,7 +116,7 @@ class SensitivityAAPSPlugin @Inject constructor(
val sensResult: String
aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity")
Arrays.sort(deviations)
val percentile = IobCobCalculatorPlugin.percentile(deviations, 0.50)
val percentile = Percentile.percentile(deviations, 0.50)
val basalOff = percentile * (60.0 / 5.0) / sens
val ratio = 1 + basalOff / profile.getMaxDailyBasal()
sensResult = when {
@ -126,13 +126,16 @@ class SensitivityAAPSPlugin @Inject constructor(
}
aapsLogger.debug(LTag.AUTOSENS, sensResult)
val output = fillResult(ratio, current.cob, pastSensitivity, ratioLimit,
sensResult, deviationsArray.size)
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)
+ dateUtil.dateAndTimeString(toTime) +
" ratio: " + output.ratio
+ " mealCOB: " + current.cob
)
aapsLogger.debug(LTag.AUTOSENS, "Sensitivity to: deviations " + deviations.contentToString())
return output
}
@ -158,7 +161,10 @@ class SensitivityAAPSPlugin @Inject constructor(
override fun applyConfiguration(configuration: JSONObject) {
try {
if (configuration.has(rh.gs(R.string.key_absorption_maxtime))) sp.putDouble(R.string.key_absorption_maxtime, configuration.getDouble(rh.gs(R.string.key_absorption_maxtime)))
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_period))) sp.putDouble(R.string.key_openapsama_autosens_period, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_period)))
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_period))) sp.putDouble(
R.string.key_openapsama_autosens_period,
configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_period))
)
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_max)))
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_min)))
} catch (e: JSONException) {

View file

@ -12,19 +12,19 @@ import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.interfaces.Sensitivity.SensitivityType
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.androidaps.plugins.aps.openAPSSMB.SMBDefaults
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensDataStore
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.AutosensResult
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.plugins.utils.Percentile
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import java.util.Arrays
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.roundToInt
@ -75,7 +75,7 @@ class SensitivityOref1Plugin @Inject constructor(
//[0] = 8 hour
//[1] = 24 hour
//deviationsHour has DeviationsArray
val deviationsHour = mutableListOf(ArrayList<Double>(), ArrayList<Double>())
val deviationsHour = mutableListOf(ArrayList(), ArrayList<Double>())
val pastSensitivityArray = mutableListOf("", "")
val sensResultArray = mutableListOf("", "")
val ratioArray = mutableListOf(0.0, 0.0)
@ -160,8 +160,8 @@ class SensitivityOref1Plugin @Inject constructor(
val sens = profile.getIsfMgdl()
aapsLogger.debug(LTag.AUTOSENS, "Records: $index $pastSensitivity")
Arrays.sort(deviations)
val pSensitive = IobCobCalculatorPlugin.percentile(deviations, 0.50)
val pResistant = IobCobCalculatorPlugin.percentile(deviations, 0.50)
val pSensitive = Percentile.percentile(deviations, 0.50)
val pResistant = Percentile.percentile(deviations, 0.50)
var basalOff = 0.0
when {
pSensitive < 0 -> { // sensitive
@ -196,9 +196,10 @@ class SensitivityOref1Plugin @Inject constructor(
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)
+ dateUtil.dateAndTimeString(toTime) +
" ratio: " + output.ratio
+ " mealCOB: " + current.cob
)
return output
}
@ -219,7 +220,10 @@ class SensitivityOref1Plugin @Inject constructor(
override fun applyConfiguration(configuration: JSONObject) {
try {
if (configuration.has(rh.gs(R.string.key_openapsama_min_5m_carbimpact))) sp.putDouble(R.string.key_openapsama_min_5m_carbimpact, configuration.getDouble(rh.gs(R.string.key_openapsama_min_5m_carbimpact)))
if (configuration.has(rh.gs(R.string.key_openapsama_min_5m_carbimpact))) sp.putDouble(
R.string.key_openapsama_min_5m_carbimpact,
configuration.getDouble(rh.gs(R.string.key_openapsama_min_5m_carbimpact))
)
if (configuration.has(rh.gs(R.string.key_absorption_cutoff))) sp.putDouble(R.string.key_absorption_cutoff, configuration.getDouble(rh.gs(R.string.key_absorption_cutoff)))
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_max))) sp.getDouble(R.string.key_openapsama_autosens_max, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_max)))
if (configuration.has(rh.gs(R.string.key_openapsama_autosens_min))) sp.getDouble(R.string.key_openapsama_autosens_min, configuration.getDouble(rh.gs(R.string.key_openapsama_autosens_min)))

View file

@ -270,8 +270,6 @@
<string name="show_queue">Show queue</string>
<string name="queue">Queue:</string>
<string name="status">Status:</string>
<string name="key_nsclientinternal_url" translatable="false">nsclientinternal_url</string>
<string name="key_nsclientinternal_api_secret" translatable="false">nsclientinternal_api_secret</string>
<string name="clearlog">Clear log</string>
<string name="key_nsclientinternal_autoscroll" translatable="false">nsclientinternal_autoscroll</string>
<string name="key_nsclientinternal_paused" translatable="false">nsclientinternal_paused</string>
@ -529,7 +527,6 @@
<string name="overview_show_basals">Basals</string>
<string name="closed_loop_disabled_on_dev_branch">Running dev version. Closed loop is disabled.</string>
<string name="engineering_mode_enabled">Engineering mode enabled</string>
<string name="profileswitch_ismissing">ProfileSwitch missing. Please do a profile switch or press \"Activate Profile\" in the LocalProfile.</string>
<string name="pumpisnottempbasalcapable">Pump is not temp basal capable</string>
<string name="closedmodedisabledinpreferences">Closed loop mode disabled in preferences</string>
<string name="autosensdisabledinpreferences">Autosens disabled in preferences</string>

View file

@ -3,10 +3,11 @@ package info.nightscout.androidaps.plugins.general.maintenance
import android.content.Context
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.TestBase
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.plugins.general.nsclient.data.NSSettingsStatus
import info.nightscout.plugins.general.maintenance.LoggerUtils
import info.nightscout.shared.sharedPreferences.SP
import org.junit.Assert
import org.junit.Before
@ -27,7 +28,7 @@ class MaintenancePluginTest : TestBase() {
@Mock lateinit var fileListProvider: PrefFileListProvider
@Mock lateinit var config: Config
lateinit var sut: MaintenancePlugin
private lateinit var sut: MaintenancePlugin
@Before
fun mock() {

View file

@ -86,6 +86,8 @@
<string name="key_rangetodisplay" translatable="false">rangetodisplay</string>
<string name="key_local_profile_last_change" translatable="false">local_profile_last_change</string>
<string name="key_ns_receive_profile_store" translatable="false">ns_receive_profile_store</string>
<string name="key_nsclientinternal_url" translatable="false">nsclientinternal_url</string>
<string name="key_nsclientinternal_api_secret" translatable="false">nsclientinternal_api_secret</string>
<!-- General-->
<string name="refresh">Refresh</string>
@ -233,6 +235,8 @@
<string name="remove_label">REMOVE</string>
<string name="activate_profile">Activate profile</string>
<string name="reset">reset</string>
<string name="profileswitch_ismissing">ProfileSwitch missing. Please do a profile switch or press \"Activate Profile\" in the LocalProfile.</string>
<string name="profile">Profile</string>
<!-- Constraints-->
<string name="limitingbasalratio">Limiting max basal rate to %1$.2f U/h because of %2$s</string>

View file

@ -1,12 +1,15 @@
package info.nightscout.androidaps.dependencyInjection
package info.nightscout.plugins.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.androidaps.plugins.general.autotune.AutotuneCore
import info.nightscout.androidaps.plugins.general.autotune.AutotuneIob
import info.nightscout.androidaps.plugins.general.autotune.AutotunePrep
import info.nightscout.androidaps.plugins.general.autotune.AutotuneFS
import info.nightscout.androidaps.plugins.general.autotune.data.*
import info.nightscout.plugins.general.autotune.AutotuneCore
import info.nightscout.plugins.general.autotune.AutotuneFS
import info.nightscout.plugins.general.autotune.AutotuneIob
import info.nightscout.plugins.general.autotune.AutotunePrep
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.BGDatum
import info.nightscout.plugins.general.autotune.data.CRDatum
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
@Module
@Suppress("unused")

View file

@ -6,7 +6,9 @@ import dagger.Module
includes = [
InsulinModule::class,
FoodModule::class,
SMSCommunicatorModule::class
SMSCommunicatorModule::class,
AutotuneModule::class,
ProfileModule::class
]
)

View file

@ -0,0 +1,12 @@
package info.nightscout.plugins.di
import dagger.Module
import dagger.android.ContributesAndroidInjector
import info.nightscout.plugins.profile.ProfileFragment
@Module
@Suppress("unused")
abstract class ProfileModule {
@ContributesAndroidInjector abstract fun contributesLocalProfileFragment(): ProfileFragment
}

View file

@ -1,15 +1,16 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.plugins.iob.iobCobCalculator.IobCobCalculatorPlugin
import info.nightscout.androidaps.utils.Round
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.plugins.utils.Percentile
import info.nightscout.shared.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.max
@Singleton
class AutotuneCore @Inject constructor(
@ -47,8 +48,7 @@ class AutotuneCore @Inject constructor(
// tune DIA
var newDia = dia
if (diaDeviations.size > 0)
{
if (diaDeviations.isNotEmpty()) {
val currentDiaMeanDev = diaDeviations[2].meanDeviation
val currentDiaRMSDev = diaDeviations[2].rmsDeviation
//Console.WriteLine(DIA,currentDIAMeanDev,currentDIARMSDev);
@ -56,35 +56,28 @@ class AutotuneCore @Inject constructor(
var minRmsDeviations = 1000000.0
var meanBest = 2
var rmsBest = 2
for (i in 0..diaDeviations.size-1)
{
for (i in diaDeviations.indices) {
val meanDeviations = diaDeviations[i].meanDeviation
val rmsDeviations = diaDeviations[i].rmsDeviation
if (meanDeviations < minMeanDeviations)
{
if (meanDeviations < minMeanDeviations) {
minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
meanBest = i
}
if (rmsDeviations < minRmsDeviations)
{
if (rmsDeviations < minRmsDeviations) {
minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
rmsBest = i
}
}
log("Best insulinEndTime for meanDeviations: ${diaDeviations[meanBest].dia} hours")
log("Best insulinEndTime for RMSDeviations: ${diaDeviations[rmsBest].dia} hours")
if (meanBest < 2 && rmsBest < 2)
{
if (meanBest < 2 && rmsBest < 2) {
if (diaDeviations[1].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[1].rmsDeviation < currentDiaRMSDev * 0.99)
newDia = diaDeviations[1].dia
}
else if (meanBest > 2 && rmsBest > 2)
{
} else if (meanBest > 2 && rmsBest > 2) {
if (diaDeviations[3].meanDeviation < currentDiaMeanDev * 0.99 && diaDeviations[3].rmsDeviation < currentDiaRMSDev * 0.99)
newDia = diaDeviations[3].dia
}
if (newDia > 12.0)
{
if (newDia > 12.0) {
log("insulinEndTime maximum is 12h: not raising further")
newDia = 12.0
}
@ -96,8 +89,7 @@ class AutotuneCore @Inject constructor(
// tune insulinPeakTime
var newPeak = peak
if (peakDeviations.size > 2)
{
if (peakDeviations.size > 2) {
val currentPeakMeanDev = peakDeviations[2].meanDeviation
val currentPeakRMSDev = peakDeviations[2].rmsDeviation
//Console.WriteLine(currentPeakMeanDev);
@ -105,37 +97,31 @@ class AutotuneCore @Inject constructor(
var minRmsDeviations = 1000000.0
var meanBest = 2
var rmsBest = 2
for (i in 0..peakDeviations.size-1)
{
val meanDeviations = peakDeviations[i].meanDeviation;
val rmsDeviations = peakDeviations[i].rmsDeviation;
if (meanDeviations < minMeanDeviations)
{
for (i in peakDeviations.indices) {
val meanDeviations = peakDeviations[i].meanDeviation
val rmsDeviations = peakDeviations[i].rmsDeviation
if (meanDeviations < minMeanDeviations) {
minMeanDeviations = Round.roundTo(meanDeviations, 0.001)
meanBest = i
}
if (rmsDeviations < minRmsDeviations)
{
if (rmsDeviations < minRmsDeviations) {
minRmsDeviations = Round.roundTo(rmsDeviations, 0.001)
rmsBest = i
}
}
log("Best insulinPeakTime for meanDeviations: ${peakDeviations[meanBest].peak} minutes")
log("Best insulinPeakTime for RMSDeviations: ${peakDeviations[rmsBest].peak} minutes")
if (meanBest < 2 && rmsBest < 2)
{
if (meanBest < 2 && rmsBest < 2) {
if (peakDeviations[1].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[1].rmsDeviation < currentPeakRMSDev * 0.99)
newPeak = peakDeviations[1].peak
}
else if (meanBest > 2 && rmsBest > 2)
{
} else if (meanBest > 2 && rmsBest > 2) {
if (peakDeviations[3].meanDeviation < currentPeakMeanDev * 0.99 && peakDeviations[3].rmsDeviation < currentPeakRMSDev * 0.99)
newPeak = peakDeviations[3].peak
}
if (newPeak != peak)
log("Adjusting insulinPeakTime from " + peak + " to " + newPeak + " minutes")
log("Adjusting insulinPeakTime from $peak to $newPeak minutes")
else
log("Leaving insulinPeakTime unchanged at " + peak)
log("Leaving insulinPeakTime unchanged at $peak")
}
// Calculate carb ratio (CR) independently of csf and isf
@ -180,22 +166,22 @@ class AutotuneCore @Inject constructor(
for (i in 0..23) {
newHourlyBasalProfile[i] = hourlyBasalProfile[i]
}
val basalUntuned = previousAutotune.basalUntuned
val basalUnTuned = previousAutotune.basalUntuned
//autotune-core (lib/autotune/index.js) #210-#266
// look at net deviations for each hour
for (hour in 0..23) {
var deviations = 0.0
for (i in basalGlucose.indices) {
val BGTime = Calendar.getInstance()
val bgTime = Calendar.getInstance()
//var BGTime: Date? = null
if (basalGlucose[i].date != 0L) {
BGTime.setTimeInMillis(basalGlucose[i].date)
bgTime.timeInMillis = basalGlucose[i].date
//BGTime = Date(basalGlucose[i].date)
} else {
log("Could not determine last BG time")
}
val myHour = BGTime.get(Calendar.HOUR_OF_DAY)
val myHour = bgTime.get(Calendar.HOUR_OF_DAY)
//val myHour = BGTime!!.hours
if (hour == myHour) {
//log.debug(basalGlucose[i].deviation);
@ -280,7 +266,7 @@ class AutotuneCore @Inject constructor(
}
//log.debug(hour, newHourlyBasalProfile);
newHourlyBasalProfile[hour] = Round.roundTo(0.8 * hourlyBasalProfile[hour] + 0.1 * newHourlyBasalProfile[lastAdjustedHour] + 0.1 * newHourlyBasalProfile[nextAdjustedHour], 0.001)
basalUntuned[hour]++
basalUnTuned[hour]++
log("Adjusting hour " + hour + " basal from " + hourlyBasalProfile[hour] + " to " + newHourlyBasalProfile[hour] + " based on hour " + lastAdjustedHour + " = " + newHourlyBasalProfile[lastAdjustedHour] + " and hour " + nextAdjustedHour + " = " + newHourlyBasalProfile[nextAdjustedHour])
} else {
lastAdjustedHour = hour
@ -301,7 +287,6 @@ class AutotuneCore @Inject constructor(
var mealCarbs = 0
var totalMealCarbs = 0
var totalDeviations = 0.0
val fullNewCSF: Double
//log.debug(CSFGlucose[0].mealAbsorption);
//log.debug(CSFGlucose[0]);
//autotune-core (lib/autotune/index.js) #346-#365
@ -321,8 +306,8 @@ class AutotuneCore @Inject constructor(
totalDeviations += deviations
} else {
//todo Philoul check 0 * min5minCarbImpact ???
deviations += Math.max(0 * min5minCarbImpact, csfGlucose[i].deviation)
mealCarbs = Math.max(mealCarbs, csfGlucose[i].mealCarbs)
deviations += max(0 * min5minCarbImpact, csfGlucose[i].deviation)
mealCarbs = max(mealCarbs, csfGlucose[i].mealCarbs)
}
}
// at midnight, write down the mealcarbs as total meal carbs (to prevent special case of when only one meal and it not finishing absorbing by midnight)
@ -334,7 +319,7 @@ class AutotuneCore @Inject constructor(
totalDeviations += deviations
}
//log.debug(totalDeviations, totalMealCarbs);
fullNewCSF = if (totalMealCarbs == 0) {
val fullNewCSF: Double = if (totalMealCarbs == 0) {
// if no meals today, csf is unchanged
csf
} else {
@ -420,22 +405,22 @@ class AutotuneCore @Inject constructor(
for (i in isfGlucose.indices) {
val deviation = isfGlucose[i].deviation
isfDeviations.add(deviation)
val BGI = isfGlucose[i].bgi
bGIs.add(BGI)
val bgi = isfGlucose[i].bgi
bGIs.add(bgi)
val avgDelta = isfGlucose[i].avgDelta
avgDeltas.add(avgDelta)
val ratio = 1 + deviation / BGI
val ratio = 1 + deviation / bgi
//log.debug("Deviation:",deviation,"BGI:",BGI,"avgDelta:",avgDelta,"ratio:",ratio);
ratios.add(ratio)
count++
}
Collections.sort(avgDeltas)
Collections.sort(bGIs)
Collections.sort(isfDeviations)
Collections.sort(ratios)
var p50deviation = IobCobCalculatorPlugin.percentile(isfDeviations.toTypedArray(), 0.50)
var p50BGI = IobCobCalculatorPlugin.percentile(bGIs.toTypedArray(), 0.50)
val p50ratios = Round.roundTo(IobCobCalculatorPlugin.percentile(ratios.toTypedArray(), 0.50), 0.001)
avgDeltas.sort()
bGIs.sort()
isfDeviations.sort()
ratios.sort()
var p50deviation = Percentile.percentile(isfDeviations.toTypedArray(), 0.50)
var p50BGI = Percentile.percentile(bGIs.toTypedArray(), 0.50)
val p50ratios = Round.roundTo(Percentile.percentile(ratios.toTypedArray(), 0.50), 0.001)
var fullNewISF = isf
if (count < 10) {
// leave isf unchanged if fewer than 5 isf data points
@ -446,13 +431,12 @@ class AutotuneCore @Inject constructor(
}
fullNewISF = Round.roundTo(fullNewISF, 0.001)
// adjust the target isf to be a weighted average of fullNewISF and pumpISF
val adjustmentFraction: Double
/*
// TODO: philoul may be allow adjustmentFraction in settings with safety limits ?)
if (typeof(pumpProfile.autotune_isf_adjustmentFraction) !== 'undefined') {
adjustmentFraction = pumpProfile.autotune_isf_adjustmentFraction;
} else {*/
adjustmentFraction = 1.0
val adjustmentFraction = 1.0
// }
// low autosens ratio = high isf
@ -501,7 +485,7 @@ class AutotuneCore @Inject constructor(
previousAutotune.basal = basalProfile
previousAutotune.isf = isf
previousAutotune.ic = Round.roundTo(carbRatio, 0.001)
previousAutotune.basalUntuned = basalUntuned
previousAutotune.basalUntuned = basalUnTuned
previousAutotune.dia = newDia
previousAutotune.peak = newPeak
val localInsulin = LocalInsulin("Ins_$newPeak-$newDia", newPeak, newDia)

View file

@ -1,13 +1,21 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.plugins.general.maintenance.LoggerUtils
import info.nightscout.androidaps.R
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.plugins.general.maintenance.LoggerUtils
import org.json.JSONException
import org.slf4j.LoggerFactory
import java.io.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
import java.io.PrintWriter
import java.text.SimpleDateFormat
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
@ -28,7 +36,6 @@ class AutotuneFS @Inject constructor(
val AAPSBOLUSESPREF = "aaps-boluses."
val PREPPEDPREF = "aaps-autotune."
val SETTINGS = "settings.json"
val PROFIL = "profil"
val PUMPPROFILE = "pumpprofile.json"
val TUNEDPROFILE = "newaapsprofile."
val LOGPREF = "autotune."

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import android.content.Context
import android.graphics.Paint
@ -20,12 +20,10 @@ import android.widget.TextView
import dagger.android.HasAndroidInjector
import dagger.android.support.DaggerFragment
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.databinding.AutotuneFragmentBinding
import info.nightscout.androidaps.dialogs.ProfileViewerDialog
import info.nightscout.androidaps.extensions.runOnUiThread
import info.nightscout.androidaps.extensions.toVisibility
@ -37,16 +35,18 @@ import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.FabricPrivacy
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.alertDialogs.OKDialog.showConfirmation
import info.nightscout.androidaps.utils.rx.AapsSchedulers
import info.nightscout.plugins.R
import info.nightscout.plugins.databinding.AutotuneFragmentBinding
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.shared.SafeParse
import info.nightscout.shared.sharedPreferences.SP
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -126,7 +126,7 @@ class AutotuneFragment : DaggerFragment() {
updateGui()
}
binding.autotuneCopylocal.setOnClickListener {
binding.autotuneCopyLocal.setOnClickListener {
val localName = rh.gs(R.string.autotune_tunedprofile_name) + " " + dateUtil.dateAndTimeString(autotunePlugin.lastRun)
val circadian = sp.getBoolean(R.string.key_autotune_circadian_ic_isf, false)
autotunePlugin.tunedProfile?.let { tunedProfile ->
@ -265,14 +265,14 @@ class AutotuneFragment : DaggerFragment() {
}
}
binding.tuneLastrun.setOnClickListener {
binding.tuneLastRun.setOnClickListener {
if (!autotunePlugin.calculationRunning) {
autotunePlugin.loadLastRun()
binding.tuneDays.value = autotunePlugin.lastNbDays.toDouble()
updateGui()
}
}
binding.tuneLastrun.paintFlags = binding.tuneLastrun.paintFlags or Paint.UNDERLINE_TEXT_FLAG
binding.tuneLastRun.paintFlags = binding.tuneLastRun.paintFlags or Paint.UNDERLINE_TEXT_FLAG
}
@Synchronized
@ -315,7 +315,7 @@ class AutotuneFragment : DaggerFragment() {
}
binding.autotuneRun.visibility = View.GONE
binding.autotuneCheckInputProfile.visibility = View.GONE
binding.autotuneCopylocal.visibility = View.GONE
binding.autotuneCopyLocal.visibility = View.GONE
binding.autotuneUpdateProfile.visibility = View.GONE
binding.autotuneRevertProfile.visibility = View.GONE
binding.autotuneProfileswitch.visibility = View.GONE
@ -326,7 +326,7 @@ class AutotuneFragment : DaggerFragment() {
}
autotunePlugin.lastRunSuccess -> {
binding.autotuneCopylocal.visibility = View.VISIBLE
binding.autotuneCopyLocal.visibility = View.VISIBLE
binding.autotuneUpdateProfile.visibility = autotunePlugin.updateButtonVisibility
binding.autotuneRevertProfile.visibility = if (autotunePlugin.updateButtonVisibility == View.VISIBLE) View.GONE else View.VISIBLE
binding.autotuneProfileswitch.visibility = View.VISIBLE
@ -339,7 +339,7 @@ class AutotuneFragment : DaggerFragment() {
binding.autotuneCheckInputProfile.visibility = View.VISIBLE
}
}
binding.tuneLastrun.text = dateUtil.dateAndTimeString(autotunePlugin.lastRun)
binding.tuneLastRun.text = dateUtil.dateAndTimeString(autotunePlugin.lastRun)
showResults()
}

View file

@ -1,29 +1,32 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import info.nightscout.androidaps.Constants
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.database.AppRepository
import info.nightscout.androidaps.database.embedments.InterfaceIDs
import info.nightscout.androidaps.database.entities.*
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.entities.ExtendedBolus
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.TemporaryBasal
import info.nightscout.androidaps.database.entities.TherapyEvent
import info.nightscout.androidaps.extensions.durationInMinutes
import info.nightscout.androidaps.extensions.iobCalc
import info.nightscout.androidaps.extensions.toJson
import info.nightscout.androidaps.extensions.toTemporaryBasal
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.ceil
@ -44,7 +47,7 @@ open class AutotuneIob @Inject constructor(
lateinit var glucose: List<GlucoseValue> // newest at index 0
private lateinit var tempBasals: ArrayList<TemporaryBasal>
var startBG: Long = 0
var endBG: Long = 0
private var endBG: Long = 0
private fun range(): Long = (60 * 60 * 1000L * dia + T.hours(2).msecs()).toLong()
fun initializeData(from: Long, to: Long, tunedProfile: ATProfile) {
@ -236,11 +239,10 @@ open class AutotuneIob @Inject constructor(
}
open fun getIOB(time: Long, localInsulin: LocalInsulin): IobTotal {
val bolusIob = getCalculationToTimeTreatments(time, localInsulin).round()
return bolusIob
return getCalculationToTimeTreatments(time, localInsulin).round()
}
fun getCalculationToTimeTreatments(time: Long, localInsulin: LocalInsulin): IobTotal {
private fun getCalculationToTimeTreatments(time: Long, localInsulin: LocalInsulin): IobTotal {
val total = IobTotal(time)
val detailedLog = sp.getBoolean(R.string.key_autotune_additional_log, false)
for (pos in boluses.indices) {
@ -257,7 +259,7 @@ open class AutotuneIob @Inject constructor(
}
fun convertToBoluses(eb: ExtendedBolus): MutableList<Bolus> {
private fun convertToBoluses(eb: ExtendedBolus): MutableList<Bolus> {
val result: MutableList<Bolus> = ArrayList()
val aboutFiveMinIntervals = ceil(eb.duration / 5.0).toInt()
val spacing = eb.duration / aboutFiveMinIntervals.toDouble()
@ -277,7 +279,7 @@ open class AutotuneIob @Inject constructor(
return result
}
fun convertToBoluses(tbr: TemporaryBasal, profile: Profile, tunedProfile: Profile): MutableList<Bolus> {
private fun convertToBoluses(tbr: TemporaryBasal, profile: Profile, tunedProfile: Profile): MutableList<Bolus> {
val result: MutableList<Bolus> = ArrayList()
val realDuration = tbr.durationInMinutes
val basalRate = profile.getBasal(tbr.timestamp)

View file

@ -1,32 +1,41 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import android.view.View
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.database.entities.UserEntry
import info.nightscout.androidaps.database.entities.ValueWithUnit
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Autotune
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.androidaps.interfaces.Insulin
import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.logging.UserEntryLogger
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.plugins.general.autotune.data.ATProfile
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.androidaps.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.T
import info.nightscout.androidaps.interfaces.BuildHelper
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.plugins.general.autotune.events.EventAutotuneUpdateGui
import info.nightscout.plugins.profile.ProfilePlugin
import info.nightscout.plugins.profile.events.EventLocalProfileChanged
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import java.util.TimeZone
import javax.inject.Inject
import javax.inject.Singleton
@ -54,17 +63,19 @@ class AutotunePlugin @Inject constructor(
private val buildHelper: BuildHelper,
private val uel: UserEntryLogger,
aapsLogger: AAPSLogger
) : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(AutotuneFragment::class.qualifiedName)
.pluginIcon(R.drawable.ic_autotune)
.pluginName(R.string.autotune)
.shortName(R.string.autotune_shortname)
.preferencesId(R.xml.pref_autotune)
.showInList(buildHelper.isEngineeringMode() && buildHelper.isDev())
.description(R.string.autotune_description),
) : PluginBase(
PluginDescription()
.mainType(PluginType.GENERAL)
.fragmentClass(AutotuneFragment::class.qualifiedName)
.pluginIcon(R.drawable.ic_autotune)
.pluginName(R.string.autotune)
.shortName(R.string.autotune_shortname)
.preferencesId(R.xml.pref_autotune)
.showInList(buildHelper.isEngineeringMode() && buildHelper.isDev())
.description(R.string.autotune_description),
aapsLogger, resourceHelper, injector
), Autotune {
@Volatile override var lastRunSuccess: Boolean = false
@Volatile var result: String = ""
@Volatile override var calculationRunning: Boolean = false
@ -106,7 +117,7 @@ class AutotunePlugin @Inject constructor(
calculationRunning = false
return
}
selectedProfile = if (profileToTune.isEmpty()) profileFunction.getProfileName() else profileToTune
selectedProfile = profileToTune.ifEmpty { profileFunction.getProfileName() }
profileFunction.getProfile()?.let { currentProfile ->
profile = profileStore.getSpecificProfile(profileToTune)?.let { ProfileSealed.Pure(it) } ?: currentProfile
}
@ -132,7 +143,7 @@ class AutotunePlugin @Inject constructor(
val from = starttime + i * 24 * 60 * 60 * 1000L // get 24 hours BG values from 4 AM to 4 AM next day
val to = from + 24 * 60 * 60 * 1000L
log("Tune day " + (i + 1) + " of " + daysBack)
tunedProfile?.let { it ->
tunedProfile?.let {
autotuneIob.initializeData(from, to, it) //autotuneIob contains BG and Treatments data from history (<=> query for ns-treatments and ns-entries)
if (autotuneIob.boluses.size == 0) {
result = rh.gs(R.string.autotune_error)
@ -209,7 +220,8 @@ class AutotunePlugin @Inject constructor(
UserEntry.Action.PROFILE_SWITCH,
UserEntry.Sources.Automation,
rh.gs(R.string.autotune),
ValueWithUnit.SimpleString(tunedP.profilename))
ValueWithUnit.SimpleString(tunedP.profilename)
)
}
rxBus.send(EventLocalProfileChanged())
}
@ -265,8 +277,8 @@ class AutotunePlugin @Inject constructor(
val jsonSettings = JSONObject()
val insulinInterface = activePlugin.activeInsulin
val utcOffset = T.msecs(TimeZone.getDefault().getOffset(dateUtil.now()).toLong()).hours()
val startDateString = dateUtil.toISOString(firstloopstart).substring(0,10)
val endDateString = dateUtil.toISOString(lastloopend - 24 * 60 * 60 * 1000L).substring(0,10)
val startDateString = dateUtil.toISOString(firstloopstart).substring(0, 10)
val endDateString = dateUtil.toISOString(lastloopend - 24 * 60 * 60 * 1000L).substring(0, 10)
val nsUrl = sp.getString(R.string.key_nsclientinternal_url, "")
val optCategorizeUam = if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) "-c=true" else ""
val optInsulinCurve = if (sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)) "-i=true" else ""
@ -291,7 +303,7 @@ class AutotunePlugin @Inject constructor(
val peaktime: Int = insulinInterface.peak
if (insulinInterface.id === Insulin.InsulinType.OREF_ULTRA_RAPID_ACTING)
jsonSettings.put("curve","ultra-rapid")
jsonSettings.put("curve", "ultra-rapid")
else if (insulinInterface.id === Insulin.InsulinType.OREF_RAPID_ACTING)
jsonSettings.put("curve", "rapid-acting")
else if (insulinInterface.id === Insulin.InsulinType.OREF_LYUMJEV) {
@ -304,7 +316,9 @@ class AutotunePlugin @Inject constructor(
jsonSettings.put("insulinPeakTime", peaktime)
}
jsonString = jsonSettings.toString(4).replace("\\/", "/")
} catch (e: JSONException) { }
} catch (e: JSONException) {
aapsLogger.error(LTag.AUTOTUNE, e.localizedMessage ?: e.toString())
}
return jsonString
}
@ -332,7 +346,7 @@ class AutotunePlugin @Inject constructor(
fun saveLastRun() {
val json = JSONObject()
json.put("lastNbDays", lastNbDays)
json.put("lastRun",lastRun)
json.put("lastRun", lastRun)
json.put("pumpProfile", pumpProfile.profile.toPureNsJson(dateUtil))
json.put("pumpProfileName", pumpProfile.profilename)
json.put("pumpPeak", pumpProfile.peak)
@ -378,13 +392,14 @@ class AutotunePlugin @Inject constructor(
atProfile.profilename = tunedProfileName
atProfile.circadianProfile = ProfileSealed.Pure(circadianTuned)
for (i in 0..23) {
atProfile.basalUntuned[i] = JsonHelper.safeGetInt(json,"missingDays_$i")
atProfile.basalUntuned[i] = JsonHelper.safeGetInt(json, "missingDays_$i")
}
}
result = JsonHelper.safeGetString(json, "result", "")
updateButtonVisibility = JsonHelper.safeGetInt(json, "updateButtonVisibility")
lastRunSuccess = true
} catch (e: Exception) {
aapsLogger.error(LTag.AUTOTUNE, e.localizedMessage ?: e.toString())
}
}

View file

@ -1,66 +1,71 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import info.nightscout.androidaps.R
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.plugins.general.autotune.data.*
import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.MidnightTime
import info.nightscout.androidaps.utils.Round
import info.nightscout.androidaps.utils.T
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.BGDatum
import info.nightscout.plugins.general.autotune.data.CRDatum
import info.nightscout.plugins.general.autotune.data.DiaDeviation
import info.nightscout.plugins.general.autotune.data.PeakDeviation
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.shared.sharedPreferences.SP
import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.pow
import kotlin.math.roundToInt
@Singleton
class AutotunePrep @Inject constructor(
private val aapsLogger: AAPSLogger,
private val sp: SP,
private val dateUtil: DateUtil,
private val autotuneFS: AutotuneFS,
private val autotuneIob: AutotuneIob
) {
fun categorize(tunedprofile: ATProfile): PreppedGlucose? {
val preppedGlucose = categorizeBGDatums(tunedprofile, tunedprofile.localInsulin)
fun categorize(tunedProfile: ATProfile): PreppedGlucose? {
val preppedGlucose = categorizeBGDatums(tunedProfile, tunedProfile.localInsulin)
val tuneInsulin = sp.getBoolean(R.string.key_autotune_tune_insulin_curve, false)
if (tuneInsulin) {
var minDeviations = 1000000.0
val diaDeviations: MutableList<DiaDeviation> = ArrayList()
val peakDeviations: MutableList<PeakDeviation> = ArrayList()
val currentDIA = tunedprofile.localInsulin.dia
val currentPeak = tunedprofile.localInsulin.peak
val currentDIA = tunedProfile.localInsulin.dia
val currentPeak = tunedProfile.localInsulin.peak
var dia = currentDIA - 2
val endDIA = currentDIA + 2
while (dia <= endDIA)
{
while (dia <= endDIA) {
var sqrtDeviations = 0.0
var deviations = 0.0
var deviationsSq = 0.0
val localInsulin = LocalInsulin("Ins_$currentPeak-$dia", currentPeak, dia)
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
val basalGlucose = curve_output?.basalGlucoseData
val curveOutput = categorizeBGDatums(tunedProfile, localInsulin, false)
val basalGlucose = curveOutput?.basalGlucoseData
basalGlucose?.let {
for (hour in 0..23) {
for (i in 0..(basalGlucose.size-1)) {
for (i in basalGlucose.indices) {
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
if (hour == myHour) {
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
deviations += Math.abs(basalGlucose[i].deviation)
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
sqrtDeviations += abs(basalGlucose[i].deviation).pow(0.5)
deviations += abs(basalGlucose[i].deviation)
deviationsSq += basalGlucose[i].deviation.pow(2.0)
}
}
}
val meanDeviation = Round.roundTo(Math.abs(deviations / basalGlucose.size), 0.001)
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
val meanDeviation = Round.roundTo(abs(deviations / basalGlucose.size), 0.001)
val smrDeviation = Round.roundTo((sqrtDeviations / basalGlucose.size).pow(2.0), 0.001)
val rmsDeviation = Round.roundTo((deviationsSq / basalGlucose.size).pow(0.5), 0.001)
log("insulinEndTime $dia meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
diaDeviations.add(
DiaDeviation(
@ -85,31 +90,30 @@ class AutotunePrep @Inject constructor(
minDeviations = 1000000.0
var peak = currentPeak - 10
val endPeak = currentPeak + 10
while (peak <= endPeak)
{
while (peak <= endPeak) {
var sqrtDeviations = 0.0
var deviations = 0.0
var deviationsSq = 0.0
val localInsulin = LocalInsulin("Ins_$peak-$currentDIA", peak, currentDIA)
val curve_output = categorizeBGDatums(tunedprofile, localInsulin, false)
val basalGlucose = curve_output?.basalGlucoseData
val curveOutput = categorizeBGDatums(tunedProfile, localInsulin, false)
val basalGlucose = curveOutput?.basalGlucoseData
basalGlucose?.let {
for (hour in 0..23) {
for (i in 0..(basalGlucose.size - 1)) {
for (i in basalGlucose.indices) {
val myHour = ((basalGlucose[i].date - MidnightTime.calc(basalGlucose[i].date)) / T.hours(1).msecs()).toInt()
if (hour == myHour) {
//console.error(basalGlucose[i].deviation);
sqrtDeviations += Math.pow(Math.abs(basalGlucose[i].deviation), 0.5)
deviations += Math.abs(basalGlucose[i].deviation)
deviationsSq += Math.pow(basalGlucose[i].deviation, 2.0)
sqrtDeviations += abs(basalGlucose[i].deviation).pow(0.5)
deviations += abs(basalGlucose[i].deviation)
deviationsSq += basalGlucose[i].deviation.pow(2.0)
}
}
}
val meanDeviation = Round.roundTo(deviations / basalGlucose.size, 0.001)
val smrDeviation = Round.roundTo(Math.pow(sqrtDeviations / basalGlucose.size, 2.0), 0.001)
val rmsDeviation = Round.roundTo(Math.pow(deviationsSq / basalGlucose.size, 0.5), 0.001)
val smrDeviation = Round.roundTo((sqrtDeviations / basalGlucose.size).pow(2.0), 0.001)
val rmsDeviation = Round.roundTo((deviationsSq / basalGlucose.size).pow(0.5), 0.001)
log("insulinPeakTime $peak meanDeviation: $meanDeviation SMRDeviation: $smrDeviation RMSDeviation: $rmsDeviation (mg/dL)")
peakDeviations.add(
PeakDeviation
@ -122,7 +126,7 @@ class AutotunePrep @Inject constructor(
)
}
deviations = Round.roundTo(deviations, 0.001);
deviations = Round.roundTo(deviations, 0.001)
if (deviations < minDeviations)
minDeviations = Round.roundTo(deviations, 0.001)
peak += 5
@ -136,7 +140,7 @@ class AutotunePrep @Inject constructor(
}
// private static Logger log = LoggerFactory.getLogger(AutotunePlugin.class);
fun categorizeBGDatums(tunedprofile: ATProfile, localInsulin: LocalInsulin, verbose: Boolean = true): PreppedGlucose? {
fun categorizeBGDatums(tunedProfile: ATProfile, localInsulin: LocalInsulin, verbose: Boolean = true): PreppedGlucose? {
//lib/meals is called before to get only meals data (in AAPS it's done in AutotuneIob)
val treatments: MutableList<Carbs> = autotuneIob.meals
val boluses: MutableList<Bolus> = autotuneIob.boluses
@ -148,14 +152,14 @@ class AutotunePrep @Inject constructor(
glucoseData.add(glucose[i])
}
}
if (glucose.size == 0 || glucoseData.size == 0 ) {
if (glucose.isEmpty() || glucoseData.size == 0) {
//aapsLogger.debug(LTag.AUTOTUNE, "No BG value received")
if (verbose)
log("No BG value received")
return null
}
glucoseData.sortWith(object: Comparator<GlucoseValue>{ override fun compare(o1: GlucoseValue, o2: GlucoseValue): Int = (o2.timestamp - o1.timestamp).toInt() })
glucoseData.sortWith { o1, o2 -> (o2.timestamp - o1.timestamp).toInt() }
// Bloc below replace bloc between #55 and #71
// boluses and maxCarbs not used here ?,
@ -189,10 +193,10 @@ class AutotunePrep @Inject constructor(
var k = 0 // index of first value used by bucket
//for loop to validate and bucket the data
for (i in 1 until glucoseData.size) {
val BGTime = glucoseData[i].timestamp
val bgTime = glucoseData[i].timestamp
val lastBGTime = glucoseData[k].timestamp
val elapsedMinutes = (BGTime - lastBGTime) / (60 * 1000)
if (Math.abs(elapsedMinutes) >= 2) {
val elapsedMinutes = (bgTime - lastBGTime) / (60 * 1000)
if (abs(elapsedMinutes) >= 2) {
//j++; // move to next bucket
k = i // store index of first value used by bucket
bucketedData.add(BGDatum(glucoseData[i], dateUtil))
@ -229,14 +233,14 @@ class AutotunePrep @Inject constructor(
for (i in bucketedData.size - 5 downTo 1) {
val glucoseDatum = bucketedData[i]
//log.debug(glucoseDatum);
val BGTime = glucoseDatum.date
val bgTime = glucoseDatum.date
// As we're processing each data point, go through the treatment.carbs and see if any of them are older than
// the current BG data point. If so, add those carbs to COB.
val treatment = if (treatments.size > 0) treatments[treatments.size - 1] else null
var myCarbs = 0.0
if (treatment != null) {
if (treatment.timestamp < BGTime) {
if (treatment.timestamp < bgTime) {
if (treatment.amount > 0.0) {
mealCOB += treatment.amount
mealCarbs += treatment.amount
@ -267,7 +271,7 @@ class AutotunePrep @Inject constructor(
glucoseDatum.avgDelta = avgDelta
//sens = ISF
val sens = tunedprofile.isf
val sens = tunedProfile.isf
// for IOB calculations, use the average of the last 4 hours' basals to help convergence;
// this helps since the basal this hour could be different from previous, especially if with autotune they start to diverge.
@ -280,7 +284,7 @@ class AutotunePrep @Inject constructor(
currentPumpBasal = Round.roundTo(currentPumpBasal / 4, 0.001) //CurrentPumpBasal for iob calculation is average of 4 last pumpProfile Basal rate
*/
// this is the current autotuned basal, used for everything else besides IOB calculations
val currentBasal = tunedprofile.getBasal(BGTime)
val currentBasal = tunedProfile.getBasal(bgTime)
// basalBGI is BGI of basal insulin activity.
val basalBGI = Round.roundTo(currentBasal * sens / 60 * 5, 0.01) // U/hr * mg/dL/U * 1 hr / 60 minutes * 5 = mg/dL/5m
@ -289,14 +293,14 @@ class AutotunePrep @Inject constructor(
//var iob = getIOB(IOBInputs)[0];
// in autotune iob is calculated with 6 hours of history data, tunedProfile and average pumpProfile basal rate...
//log("currentBasal: " + currentBasal + " BGTime: " + BGTime + " / " + dateUtil!!.timeStringWithSeconds(BGTime) + "******************************************************************************************")
val iob = autotuneIob.getIOB(BGTime, localInsulin) // add localInsulin to be independent to InsulinPlugin
val iob = autotuneIob.getIOB(bgTime, localInsulin) // add localInsulin to be independent to InsulinPlugin
// activity times ISF times 5 minutes is BGI
val BGI = Round.roundTo(-iob.activity * sens * 5, 0.01)
val bgi = Round.roundTo(-iob.activity * sens * 5, 0.01)
// datum = one glucose data point (being prepped to store in output)
glucoseDatum.bgi = BGI
glucoseDatum.bgi = bgi
// calculating deviation
var deviation = avgDelta - BGI
var deviation = avgDelta - bgi
// set positive deviations to zero if BG is below 80
if (bg < 80 && deviation > 0) {
@ -309,10 +313,10 @@ class AutotunePrep @Inject constructor(
// Then, calculate carb absorption for that 5m interval using the deviation.
if (mealCOB > 0) {
val ci = Math.max(deviation, sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, 3.0))
val absorbed = ci * tunedprofile.ic / sens
val ci = max(deviation, sp.getDouble(R.string.key_openapsama_min_5m_carbimpact, 3.0))
val absorbed = ci * tunedProfile.ic / sens
// Store the COB, and use it as the starting point for the next data point.
mealCOB = Math.max(0.0, mealCOB - absorbed)
mealCOB = max(0.0, mealCOB - absorbed)
}
// Calculate carb ratio (CR) independently of CSF and ISF
@ -323,7 +327,7 @@ class AutotunePrep @Inject constructor(
if (mealCOB > 0 || calculatingCR) {
// set initial values when we first see COB
crCarbs += myCarbs
if (calculatingCR == false) {
if (!calculatingCR) {
crInitialIOB = iob.iob
crInitialBG = glucoseDatum.value
crInitialCarbTime = glucoseDatum.date
@ -354,13 +358,13 @@ class AutotunePrep @Inject constructor(
crDatum.crCarbs = crCarbs
//log.debug(CRDatum);
//String crDataString = "{\"CRInitialIOB\": " + CRInitialIOB + ", \"CRInitialBG\": " + CRInitialBG + ", \"CRInitialCarbTime\": " + CRInitialCarbTime + ", \"CREndIOB\": " + CREndIOB + ", \"CREndBG\": " + CREndBG + ", \"CREndTime\": " + CREndTime + ", \"CRCarbs\": " + CRCarbs + "}";
val CRElapsedMinutes = Math.round((crEndTime - crInitialCarbTime) / (1000 * 60).toFloat())
val crElapsedMinutes = ((crEndTime - crInitialCarbTime) / (1000 * 60).toFloat()).roundToInt()
//log.debug(CREndTime - CRInitialCarbTime, CRElapsedMinutes);
if (CRElapsedMinutes < 60 || i == 1 && mealCOB > 0) {
if (crElapsedMinutes < 60 || i == 1 && mealCOB > 0) {
//aapsLogger.debug(LTag.AUTOTUNE, "Ignoring $CRElapsedMinutes m CR period.")
if (verbose)
log("Ignoring $CRElapsedMinutes m CR period.")
log("Ignoring $crElapsedMinutes m CR period.")
} else {
crData.add(crDatum)
}
@ -376,11 +380,7 @@ class AutotunePrep @Inject constructor(
absorbing = if (iob.iob < currentBasal / 2) {
false
// otherwise, as long as deviations are positive, keep tracking carb deviations
} else if (deviation > 0) {
true
} else {
false
}
} else deviation > 0
if (!absorbing && mealCOB == 0.0) {
mealCarbs = 0.0
}
@ -405,11 +405,7 @@ class AutotunePrep @Inject constructor(
log("${csfGlucoseData[csfGlucoseData.size - 1].mealAbsorption} carb absorption")
}
if (iob.iob > 2 * currentBasal || deviation > 6 || uam) {
uam = if (deviation > 0) {
true
} else {
false
}
uam = deviation > 0
if (type != "uam") {
glucoseDatum.uamAbsorption = "start"
//aapsLogger.debug(LTag.AUTOTUNE, "${glucoseDatum.uamAbsorption} unannnounced meal absorption")
@ -431,11 +427,11 @@ class AutotunePrep @Inject constructor(
// When BGI is smaller than about 1/4 of basalBGI, we want to use that data to tune basals
// When BGI is negative and more than about 1/4 of basalBGI, we can use that data to tune ISF,
// unless avgDelta is positive: then that's some sort of unexplained rise we don't want to use for ISF, so that means basals
if (basalBGI > -4 * BGI) {
if (basalBGI > -4 * bgi) {
type = "basal"
basalGlucoseData.add(glucoseDatum)
} else {
if (avgDelta > 0 && avgDelta > -2 * BGI) {
if (avgDelta > 0 && avgDelta > -2 * bgi) {
//type="unknown"
type = "basal"
basalGlucoseData.add(glucoseDatum)
@ -449,8 +445,12 @@ class AutotunePrep @Inject constructor(
// debug line to print out all the things
//aapsLogger.debug(LTag.AUTOTUNE, "${(if (absorbing) 1 else 0)} mealCOB: ${Round.roundTo(mealCOB, 0.1)} mealCarbs: ${Math.round(mealCarbs)} basalBGI: ${Round.roundTo(basalBGI, 0.1)} BGI: ${Round.roundTo(BGI, 0.1)} IOB: ${iob.iob} Activity: ${iob.activity} at ${dateUtil.timeStringWithSeconds(BGTime)} dev: $deviation avgDelta: $avgDelta $type")
if (verbose)
log("${(if (absorbing) 1 else 0)} mealCOB: ${Round.roundTo(mealCOB, 0.1)} mealCarbs: ${Math.round(mealCarbs)} basalBGI: ${Round.roundTo(basalBGI, 0.1)} BGI: ${Round
.roundTo(BGI, 0.1)} IOB: ${iob.iob} Activity: ${iob.activity} at ${dateUtil.timeStringWithSeconds(BGTime)} dev: $deviation avgDelta: $avgDelta $type")
log(
"${(if (absorbing) 1 else 0)} mealCOB: ${Round.roundTo(mealCOB, 0.1)} mealCarbs: ${mealCarbs.roundToInt()} basalBGI: ${Round.roundTo(basalBGI, 0.1)} BGI: ${
Round
.roundTo(bgi, 0.1)
} IOB: ${iob.iob} Activity: ${iob.activity} at ${dateUtil.timeStringWithSeconds(bgTime)} dev: $deviation avgDelta: $avgDelta $type"
)
}
//****************************************************************************************************************************************
@ -460,33 +460,33 @@ class AutotunePrep @Inject constructor(
crDatum.crInsulin = dosed(crDatum.crInitialCarbTime, crDatum.crEndTime, boluses)
}
// categorize.js Lines 384-436
val CSFLength = csfGlucoseData.size
var ISFLength = isfGlucoseData.size
val UAMLength = uamGlucoseData.size
val csfLength = csfGlucoseData.size
var isfLength = isfGlucoseData.size
val uamLength = uamGlucoseData.size
var basalLength = basalGlucoseData.size
if (sp.getBoolean(R.string.key_autotune_categorize_uam_as_basal, false)) {
//aapsLogger.debug(LTag.AUTOTUNE, "Categorizing all UAM data as basal.")
if (verbose)
log("Categorizing all UAM data as basal.")
basalGlucoseData.addAll(uamGlucoseData)
} else if (CSFLength > 12) {
} else if (csfLength > 12) {
//aapsLogger.debug(LTag.AUTOTUNE, "Found at least 1h of carb: assuming meals were announced, and categorizing UAM data as basal.")
if (verbose)
log("Found at least 1h of carb: assuming meals were announced, and categorizing UAM data as basal.")
basalGlucoseData.addAll(uamGlucoseData)
} else {
if (2 * basalLength < UAMLength) {
if (2 * basalLength < uamLength) {
//log.debug(basalGlucoseData, UAMGlucoseData);
//aapsLogger.debug(LTag.AUTOTUNE, "Warning: too many deviations categorized as UnAnnounced Meals")
//aapsLogger.debug(LTag.AUTOTUNE, "Adding $UAMLength UAM deviations to $basalLength basal ones")
if (verbose) {
log("Warning: too many deviations categorized as UnAnnounced Meals")
log("Adding $UAMLength UAM deviations to $basalLength basal ones")
log("Adding $uamLength UAM deviations to $basalLength basal ones")
}
basalGlucoseData.addAll(uamGlucoseData)
//log.debug(basalGlucoseData);
// if too much data is excluded as UAM, add in the UAM deviations, but then discard the highest 50%
basalGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort
basalGlucoseData.sortWith { o1, o2 -> (100 * o1.deviation - 100 * o2.deviation).toInt() } //deviation rouded to 0.01, so *100 to avoid crash during sort
val newBasalGlucose: MutableList<BGDatum> = ArrayList()
for (i in 0 until basalGlucoseData.size / 2) {
newBasalGlucose.add(basalGlucoseData[i])
@ -497,13 +497,13 @@ class AutotunePrep @Inject constructor(
if (verbose)
log("and selecting the lowest 50%, leaving ${basalGlucoseData.size} basal+UAM ones")
}
if (2 * ISFLength < UAMLength) {
if (2 * isfLength < uamLength) {
//aapsLogger.debug(LTag.AUTOTUNE, "Adding $UAMLength UAM deviations to $ISFLength ISF ones")
if (verbose)
log("Adding $UAMLength UAM deviations to $ISFLength ISF ones")
log("Adding $uamLength UAM deviations to $isfLength ISF ones")
isfGlucoseData.addAll(uamGlucoseData)
// if too much data is excluded as UAM, add in the UAM deviations to ISF, but then discard the highest 50%
isfGlucoseData.sortWith(object: Comparator<BGDatum>{ override fun compare(o1: BGDatum, o2: BGDatum): Int = (100 * o1.deviation - 100 * o2.deviation).toInt() }) //deviation rouded to 0.01, so *100 to avoid crash during sort
isfGlucoseData.sortWith { o1, o2 -> (100 * o1.deviation - 100 * o2.deviation).toInt() } //deviation rouded to 0.01, so *100 to avoid crash during sort
val newISFGlucose: MutableList<BGDatum> = ArrayList()
for (i in 0 until isfGlucoseData.size / 2) {
newISFGlucose.add(isfGlucoseData[i])
@ -517,15 +517,15 @@ class AutotunePrep @Inject constructor(
}
}
basalLength = basalGlucoseData.size
ISFLength = isfGlucoseData.size
if (4 * basalLength + ISFLength < CSFLength && ISFLength < 10) {
isfLength = isfGlucoseData.size
if (4 * basalLength + isfLength < csfLength && isfLength < 10) {
//aapsLogger.debug(LTag.AUTOTUNE, "Warning: too many deviations categorized as meals")
//aapsLogger.debug(LTag.AUTOTUNE, "Adding $CSFLength CSF deviations to $ISFLength ISF ones")
if (verbose) {
log("Warning: too many deviations categorized as meals")
//log.debug("Adding",CSFLength,"CSF deviations to",basalLength,"basal ones");
//var basalGlucoseData = basalGlucoseData.concat(CSFGlucoseData);
log("Adding $CSFLength CSF deviations to $ISFLength ISF ones")
log("Adding $csfLength CSF deviations to $isfLength ISF ones")
}
isfGlucoseData.addAll(csfGlucoseData)
csfGlucoseData = ArrayList()
@ -543,7 +543,7 @@ class AutotunePrep @Inject constructor(
private fun dosed(start: Long, end: Long, treatments: List<Bolus>): Double {
var insulinDosed = 0.0
//aapsLogger.debug(LTag.AUTOTUNE, "No treatments to process.")
if (treatments.size == 0) {
if (treatments.isEmpty()) {
log("No treatments to process.")
return 0.0
}

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.core.R
@ -8,7 +8,14 @@ import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.database.data.Block
import info.nightscout.androidaps.extensions.blockValueBySeconds
import info.nightscout.androidaps.extensions.pureProfileFromJson
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.interfaces.ActivePlugin
import info.nightscout.androidaps.interfaces.Config
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.Insulin
import info.nightscout.androidaps.interfaces.Profile
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.interfaces.ProfileStore
import info.nightscout.androidaps.interfaces.ResourceHelper
import info.nightscout.androidaps.plugins.bus.RxBus
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.Round
@ -19,7 +26,7 @@ import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.text.DecimalFormat
import java.util.*
import java.util.TimeZone
import javax.inject.Inject
class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector: HasAndroidInjector) {
@ -56,18 +63,19 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
val avgIC: Double
get() = if (profile.getIcsValues().size == 1) profile.getIcsValues().get(0).value else Round.roundTo(averageProfileValue(profile.getIcsValues()), 0.01)
fun getBasal(timestamp: Long): Double = basal[Profile.secondsFromMidnight(timestamp)/3600]
fun getBasal(timestamp: Long): Double = basal[Profile.secondsFromMidnight(timestamp) / 3600]
// for localProfilePlugin Synchronisation
fun basal() = jsonArray(basal)
fun ic(circadian: Boolean = false): JSONArray {
if(circadian)
return jsonArray(pumpProfile.icBlocks, avgIC/pumpProfileAvgIC)
if (circadian)
return jsonArray(pumpProfile.icBlocks, avgIC / pumpProfileAvgIC)
return jsonArray(ic)
}
fun isf(circadian: Boolean = false): JSONArray {
if(circadian)
return jsonArray(pumpProfile.isfBlocks, avgISF/pumpProfileAvgISF)
if (circadian)
return jsonArray(pumpProfile.isfBlocks, avgISF / pumpProfileAvgISF)
return jsonArray(Profile.fromMgdlToUnits(isf, profile.units))
}
@ -88,7 +96,7 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
fun profiletoOrefJSON(): String {
var jsonString = ""
val json = JSONObject()
val insulinInterface: Insulin = activePlugin.activeInsulin
val insulinInterface: Insulin = activePlugin.activeInsulin
try {
json.put("name", profilename)
json.put("min_5m_carbimpact", sp.getDouble("openapsama_min_5m_carbimpact", 3.0))
@ -115,7 +123,8 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
JSONObject()
.put("start", time)
.put("minutes", h * 60)
.put("rate", profile.getBasalTimeFromMidnight(secondfrommidnight)
.put(
"rate", profile.getBasalTimeFromMidnight(secondfrommidnight)
)
)
}
@ -134,7 +143,8 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
json.put("units", GlucoseUnit.MGDL.asText)
json.put("timezone", TimeZone.getDefault().id)
jsonString = json.toString(2).replace("\\/", "/")
} catch (e: JSONException) {}
} catch (e: JSONException) {
}
return jsonString
}
@ -144,8 +154,8 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
try {
json.put("dia", dia)
if (circadian) {
json.put("sens", jsonArray(pumpProfile.isfBlocks, avgISF/pumpProfileAvgISF))
json.put("carbratio", jsonArray(pumpProfile.icBlocks, avgIC/pumpProfileAvgIC))
json.put("sens", jsonArray(pumpProfile.isfBlocks, avgISF / pumpProfileAvgISF))
json.put("carbratio", jsonArray(pumpProfile.icBlocks, avgIC / pumpProfileAvgIC))
} else {
json.put("sens", jsonArray(Profile.fromMgdlToUnits(isf, profile.units)))
json.put("carbratio", jsonArray(ic))
@ -156,8 +166,7 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
return pureProfileFromJson(json, dateUtil, profile.units.asText)
}
fun profileStore(circadian: Boolean = false): ProfileStore?
{
fun profileStore(circadian: Boolean = false): ProfileStore? {
var profileStore: ProfileStore? = null
val json = JSONObject()
val store = JSONObject()
@ -170,7 +179,8 @@ class ATProfile(profile: Profile, var localInsulin: LocalInsulin, val injector:
json.put("store", store)
json.put("startDate", dateUtil.toISOAsUTC(dateUtil.now()))
profileStore = ProfileStore(injector, json, dateUtil)
} catch (e: JSONException) { }
} catch (e: JSONException) {
}
return profileStore
}

View file

@ -1,17 +1,18 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import info.nightscout.androidaps.database.entities.GlucoseValue.TrendArrow
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.database.entities.GlucoseValue.TrendArrow
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.T
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import java.util.TimeZone
/**
* Created by Rumen Georgiev on 2/24/2018.
*/
class BGDatum {
//Added by Rumen for autotune
var id: Long = 0
var date = 0L
@ -27,7 +28,10 @@ class BGDatum {
private set
var dateUtil: DateUtil
constructor(dateUtil: DateUtil) { this.dateUtil = dateUtil}
constructor(dateUtil: DateUtil) {
this.dateUtil = dateUtil
}
constructor(json: JSONObject, dateUtil: DateUtil) {
this.dateUtil = dateUtil
try {

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONException
@ -20,7 +20,10 @@ class CRDatum {
var crInsulinTotal = 0.0
var dateUtil: DateUtil
constructor(dateUtil: DateUtil) { this.dateUtil = dateUtil}
constructor(dateUtil: DateUtil) {
this.dateUtil = dateUtil
}
constructor(json: JSONObject, dateUtil: DateUtil) {
this.dateUtil = dateUtil
try {

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import org.json.JSONException
import org.json.JSONObject

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import org.json.JSONException
import org.json.JSONObject

View file

@ -1,10 +1,9 @@
package info.nightscout.androidaps.plugins.general.autotune.data
package info.nightscout.plugins.general.autotune.data
import info.nightscout.androidaps.utils.DateUtil
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
import java.util.*
class PreppedGlucose {

View file

@ -1,4 +1,4 @@
package info.nightscout.androidaps.plugins.general.autotune.events
package info.nightscout.plugins.general.autotune.events
import info.nightscout.androidaps.events.Event

View file

@ -1,6 +1,7 @@
package info.nightscout.androidaps.plugins.general.maintenance
package info.nightscout.plugins.general.maintenance
import info.nightscout.androidaps.annotations.OpenForTesting
import info.nightscout.androidaps.plugins.general.maintenance.PrefFileListProvider
import javax.inject.Inject
import javax.inject.Singleton
@ -21,15 +22,15 @@ class LoggerUtils @Inject constructor(
*
* @return
*/
/*
This is failing after slf4j update to 2.0.0
It would be better to find a way to read the value from xml
So far replaced by static value
val logDirectory: String
get() {
val lc = LoggerFactory.getILoggerFactory() as LoggerContext
return lc.getProperty("EXT_FILES_DIR")
}
*/
/*
This is failing after slf4j update to 2.0.0
It would be better to find a way to read the value from xml
So far replaced by static value
val logDirectory: String
get() {
val lc = LoggerFactory.getILoggerFactory() as LoggerContext
return lc.getProperty("EXT_FILES_DIR")
}
*/
val logDirectory get() = prefFileListProvider.logsPath
}

View file

@ -0,0 +1,20 @@
package info.nightscout.plugins.utils
import kotlin.math.floor
object Percentile {
// From https://gist.github.com/IceCreamYou/6ffa1b18c4c8f6aeaad2
// Returns the value at a given percentile in a sorted numeric array.
// "Linear interpolation between closest ranks" method
fun percentile(arr: Array<Double>, p: Double): Double {
if (arr.isEmpty()) return 0.0
if (p <= 0) return arr[0]
if (p >= 1) return arr[arr.size - 1]
val index = arr.size * p
val lower = floor(index)
val upper = lower + 1
val weight = index % 1
return if (upper >= arr.size) arr[lower.toInt()] else arr[lower.toInt()] * (1 - weight) + arr[upper.toInt()] * weight
}
}

View file

@ -3,7 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".plugins.general.autotune.AutotuneFragment">
tools:context="info.nightscout.plugins.general.autotune.AutotuneFragment">
<LinearLayout
android:layout_width="match_parent"
@ -133,7 +133,7 @@
android:textSize="14sp" />
<TextView
android:id="@+id/tune_lastrun"
android:id="@+id/tune_last_run"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
@ -246,7 +246,7 @@
app:layout_row="0" />
<com.google.android.material.button.MaterialButton
android:id="@+id/autotune_copylocal"
android:id="@+id/autotune_copy_local"
style="@style/GrayButton"
android:layout_width="0dp"
android:layout_height="wrap_content"

View file

@ -1,18 +1,19 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.data.LocalInsulin
import info.nightscout.androidaps.data.ProfileSealed
import info.nightscout.androidaps.data.PureProfile
import info.nightscout.androidaps.database.data.Block
import info.nightscout.androidaps.database.data.TargetBlock
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.general.autotune.data.*
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONArray
import org.json.JSONObject
@ -22,14 +23,13 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import java.io.File
import java.util.*
import java.util.TimeZone
class AutotuneCoreTest : TestBaseWithProfile() {
@Mock lateinit var sp: SP
@Mock lateinit var autotuneFS: AutotuneFS
@Mock lateinit var injector: HasAndroidInjector
@Mock lateinit var activePlugin: ActivePlugin
private lateinit var autotuneCore: AutotuneCore
private var min5mCarbImpact = 0.0
private var autotuneMin = 0.0

View file

@ -1,7 +1,6 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import dagger.android.HasAndroidInjector
import info.nightscout.androidaps.R
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.data.IobTotal
import info.nightscout.androidaps.data.LocalInsulin
@ -14,11 +13,14 @@ import info.nightscout.androidaps.database.entities.Bolus
import info.nightscout.androidaps.database.entities.Carbs
import info.nightscout.androidaps.database.entities.GlucoseValue
import info.nightscout.androidaps.extensions.shiftBlock
import info.nightscout.androidaps.interfaces.*
import info.nightscout.androidaps.plugins.general.autotune.data.*
import info.nightscout.androidaps.interfaces.GlucoseUnit
import info.nightscout.androidaps.interfaces.ProfileFunction
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.JsonHelper
import info.nightscout.androidaps.utils.T
import info.nightscout.plugins.R
import info.nightscout.plugins.general.autotune.data.ATProfile
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.sharedPreferences.SP
import org.json.JSONArray
@ -29,14 +31,13 @@ import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import java.io.File
import java.util.*
import java.util.TimeZone
class AutotunePrepTest : TestBaseWithProfile() {
@Mock lateinit var sp: SP
@Mock lateinit var autotuneFS: AutotuneFS
@Mock lateinit var injector: HasAndroidInjector
@Mock lateinit var activePlugin: ActivePlugin
@Mock lateinit var repository: AppRepository
private lateinit var autotunePrep: AutotunePrep
private lateinit var autotuneIob: TestAutotuneIob
@ -55,8 +56,8 @@ class AutotunePrepTest : TestBaseWithProfile() {
fun autotunePrepTest1() { // Test if categorisation with standard treatments with carbs is Ok
val inputIobJson = File("src/test/res/autotune/test1/oaps-iobCalc.2022-05-21.json").readText() //json files build with iob/activity calculated by OAPS
val iobOapsCalculation = buildIobOaps(JSONArray(inputIobJson))
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, activePlugin, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(aapsLogger, sp, dateUtil, autotuneFS, autotuneIob)
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(sp, dateUtil, autotuneFS, autotuneIob)
val inputProfileJson = File("src/test/res/autotune/test1/profile.pump.json").readText()
val inputProfile = atProfileFromOapsJson(JSONObject(inputProfileJson), dateUtil)!!
val prepJson = File("src/test/res/autotune/test1/autotune.2022-05-21.json").readText()
@ -94,8 +95,8 @@ class AutotunePrepTest : TestBaseWithProfile() {
fun autotunePrepTest2() { // Test if categorisation without carbs (full UAM) and categorize UAM as basal false is Ok
val inputIobJson = File("src/test/res/autotune/test2/oaps-iobCalc.2022-05-21.json").readText() //json files build with iob/activity calculated by OAPS
val iobOapsCalculation = buildIobOaps(JSONArray(inputIobJson))
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, activePlugin, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(aapsLogger, sp, dateUtil, autotuneFS, autotuneIob)
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(sp, dateUtil, autotuneFS, autotuneIob)
val inputProfileJson = File("src/test/res/autotune/test2/profile.pump.json").readText()
val inputProfile = atProfileFromOapsJson(JSONObject(inputProfileJson), dateUtil)!!
val prepJson = File("src/test/res/autotune/test2/autotune.2022-05-21.json").readText()
@ -133,8 +134,8 @@ class AutotunePrepTest : TestBaseWithProfile() {
fun autotunePrepTest3() { // Test if categorisation without carbs (full UAM) and categorize UAM as basal true is Ok
val inputIobJson = File("src/test/res/autotune/test3/oaps-iobCalc.2022-05-21.json").readText() //json files build with iob/activity calculated by OAPS
val iobOapsCalculation = buildIobOaps(JSONArray(inputIobJson))
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, activePlugin, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(aapsLogger, sp, dateUtil, autotuneFS, autotuneIob)
autotuneIob = TestAutotuneIob(aapsLogger, repository, profileFunction, sp, dateUtil, autotuneFS, iobOapsCalculation)
autotunePrep = AutotunePrep(sp, dateUtil, autotuneFS, autotuneIob)
val inputProfileJson = File("src/test/res/autotune/test3/profile.pump.json").readText()
val inputProfile = atProfileFromOapsJson(JSONObject(inputProfileJson), dateUtil)!!
val prepJson = File("src/test/res/autotune/test3/autotune.2022-05-21.json").readText()
@ -305,10 +306,9 @@ class AutotunePrepTest : TestBaseWithProfile() {
class TestAutotuneIob(
val aapsLogger: AAPSLogger,
repository: AppRepository,
val profileFunction: ProfileFunction,
val sp: SP,
val dateUtil: DateUtil,
val activePlugin: ActivePlugin,
profileFunction: ProfileFunction,
sp: SP,
dateUtil: DateUtil,
autotuneFS: AutotuneFS,
private val iobOapsCalculation: ArrayList<IobTotal>
) : AutotuneIob(

View file

@ -1,9 +1,9 @@
package info.nightscout.androidaps.plugins.general.autotune
package info.nightscout.plugins.general.autotune
import info.nightscout.androidaps.TestBaseWithProfile
import info.nightscout.androidaps.plugins.general.autotune.data.BGDatum
import info.nightscout.androidaps.plugins.general.autotune.data.CRDatum
import info.nightscout.androidaps.plugins.general.autotune.data.PreppedGlucose
import info.nightscout.plugins.general.autotune.data.BGDatum
import info.nightscout.plugins.general.autotune.data.CRDatum
import info.nightscout.plugins.general.autotune.data.PreppedGlucose
import org.json.JSONObject
import org.junit.Assert
import org.junit.Before

View file

@ -28,7 +28,6 @@
<string name="invalid_weight">Invalid weight entry</string>
<string name="id">ID:</string>
<string name="submit">Submit</string>
<string name="profile">Profile</string>
<string name="age">Age</string>
<string name="weight_label">Weight</string>
<string name="most_common_profile">Most common profile:</string>