From d5af299692efb0e05762dc0ccf1f38f49836cbdf Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Sun, 4 Dec 2022 23:46:06 +0100 Subject: [PATCH] lift out flat BG check from determinebasal to BgQualityCheckPlugin --- .../interfaces/ConstraintsCheckerTest.kt | 8 +- .../plugins/safety/SafetyPluginTest.kt | 4 +- .../interfaces/aps/DetermineBasalAdapter.kt | 2 +- .../bgQualityCheck/BgQualityCheck.kt | 9 ++ .../interfaces/source/DexcomBoyda.kt | 3 + .../main/assets/OpenAPSSMB/determine-basal.js | 8 +- .../OpenAPSSMBDynamicISF/determine-basal.js | 8 +- .../openAPSAMA/DetermineBasalAdapterAMAJS.kt | 2 +- .../openAPSSMB/DetermineBasalAdapterSMBJS.kt | 10 +- .../aps/openAPSSMB/OpenAPSSMBPlugin.kt | 7 +- .../DetermineBasalAdapterSMBDynamicISFJS.kt | 10 +- .../OpenAPSSMBDynamicISFPlugin.kt | 7 +- .../nightscout/plugins/source/AidexPlugin.kt | 6 - .../nightscout/plugins/source/DexcomPlugin.kt | 7 +- .../plugins/source/NSClientSourcePlugin.kt | 4 +- .../plugins/source/RandomBgPlugin.kt | 4 +- .../nightscout/plugins/source/XdripPlugin.kt | 4 +- .../bgQualityCheck/BgQualityCheckPlugin.kt | 76 +++++++++---- .../drawable/ic_baseline_trending_flat_24.xml | 5 + .../support/src/main/res/values/strings.xml | 1 + .../BgQualityCheckPluginTest.kt | 106 ++++++++++++++---- 21 files changed, 197 insertions(+), 94 deletions(-) create mode 100644 core/interfaces/src/main/java/info/nightscout/interfaces/source/DexcomBoyda.kt create mode 100644 plugins/support/src/main/res/drawable/ic_baseline_trending_flat_24.xml diff --git a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt index 4069a46c26..9547d3bb5a 100644 --- a/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/interfaces/ConstraintsCheckerTest.kt @@ -13,6 +13,7 @@ import info.nightscout.androidaps.plugins.pump.insight.LocalInsightPlugin import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.database.impl.AppRepository import info.nightscout.implementation.constraints.ConstraintsImpl +import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.constraints.Objectives @@ -71,6 +72,7 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { @Mock lateinit var profileInstantiator: ProfileInstantiator @Mock lateinit var danaHistoryDatabase: DanaHistoryDatabase @Mock lateinit var insightDatabase: InsightDatabase + @Mock lateinit var bgQualityCheck: BgQualityCheck private lateinit var hardLimits: HardLimits private lateinit var danaPump: DanaPump @@ -182,7 +184,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { sp, dateUtil, repository, - glucoseStatusProvider + glucoseStatusProvider, + bgQualityCheck ) openAPSSMBDynamicISFPlugin = OpenAPSSMBDynamicISFPlugin( @@ -201,7 +204,8 @@ class ConstraintsCheckerTest : TestBaseWithProfile() { dateUtil, repository, glucoseStatusProvider, - config + config, + bgQualityCheck ) openAPSAMAPlugin = OpenAPSAMAPlugin( diff --git a/app/src/test/java/info/nightscout/androidaps/plugins/safety/SafetyPluginTest.kt b/app/src/test/java/info/nightscout/androidaps/plugins/safety/SafetyPluginTest.kt index cfd4477aa7..644d36b923 100644 --- a/app/src/test/java/info/nightscout/androidaps/plugins/safety/SafetyPluginTest.kt +++ b/app/src/test/java/info/nightscout/androidaps/plugins/safety/SafetyPluginTest.kt @@ -7,6 +7,7 @@ import info.nightscout.androidaps.TestBaseWithProfile import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.database.impl.AppRepository import info.nightscout.interfaces.Constants +import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.plugin.ActivePlugin @@ -37,6 +38,7 @@ class SafetyPluginTest : TestBaseWithProfile() { @Mock lateinit var profiler: Profiler @Mock lateinit var repository: AppRepository @Mock lateinit var glucoseStatusProvider: GlucoseStatusProvider + @Mock lateinit var bgQualityCheck: BgQualityCheck private lateinit var hardLimits: HardLimits private lateinit var safetyPlugin: SafetyPlugin @@ -80,7 +82,7 @@ class SafetyPluginTest : TestBaseWithProfile() { ) openAPSSMBPlugin = OpenAPSSMBPlugin( injector, aapsLogger, rxBus, constraintChecker, rh, profileFunction, context, activePlugin, iobCobCalculator, hardLimits, profiler, sp, - dateUtil, repository, glucoseStatusProvider + dateUtil, repository, glucoseStatusProvider, bgQualityCheck ) } diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/aps/DetermineBasalAdapter.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/aps/DetermineBasalAdapter.kt index 44128b5c14..cc03ca3a3c 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/aps/DetermineBasalAdapter.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/aps/DetermineBasalAdapter.kt @@ -30,7 +30,7 @@ interface DetermineBasalAdapter { microBolusAllowed: Boolean = false, uamAllowed: Boolean = false, advancedFiltering: Boolean = false, - isSaveCgmSource: Boolean = false + flatBGsDetected: Boolean = false ) operator fun invoke(): APSResult? diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/bgQualityCheck/BgQualityCheck.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/bgQualityCheck/BgQualityCheck.kt index 929aecbe30..b1731340dd 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/bgQualityCheck/BgQualityCheck.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/bgQualityCheck/BgQualityCheck.kt @@ -3,6 +3,15 @@ package info.nightscout.interfaces.bgQualityCheck import androidx.annotation.DrawableRes interface BgQualityCheck { + enum class State { + UNKNOWN, + FIVE_MIN_DATA, + RECALCULATED, + DOUBLED, + FLAT // stale data for 45 min + } + + var state: State var message: String @DrawableRes fun icon(): Int fun stateDescription(): String diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/source/DexcomBoyda.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/source/DexcomBoyda.kt new file mode 100644 index 0000000000..e786103b4a --- /dev/null +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/source/DexcomBoyda.kt @@ -0,0 +1,3 @@ +package info.nightscout.interfaces.source + +interface DexcomBoyda \ No newline at end of file diff --git a/plugins/aps/src/main/assets/OpenAPSSMB/determine-basal.js b/plugins/aps/src/main/assets/OpenAPSSMB/determine-basal.js index f69317bfa6..9dd8aea86e 100644 --- a/plugins/aps/src/main/assets/OpenAPSSMB/determine-basal.js +++ b/plugins/aps/src/main/assets/OpenAPSSMB/determine-basal.js @@ -110,7 +110,7 @@ function enable_smb( return false; } -var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, isSaveCgmSource) { +var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, flatBGsDetected) { var rT = {}; //short for requestedTemp var deliverAt = new Date(); @@ -142,16 +142,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps - //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc - } else if ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 && !isSaveCgmSource) { + } else if ( bg > 60 && flatBGsDetected) { if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { rT.reason = "CGM was just calibrated"; } else { rT.reason = "Error: CGM data is unchanged for the past ~45m"; } } - //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc - if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) && !isSaveCgmSource ) { + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && flatBGsDetected )) { if (currenttemp.rate > basal) { // high temp is running rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; diff --git a/plugins/aps/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js b/plugins/aps/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js index fed911758e..34a3233647 100644 --- a/plugins/aps/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js +++ b/plugins/aps/src/main/assets/OpenAPSSMBDynamicISF/determine-basal.js @@ -110,7 +110,7 @@ function enable_smb( return false; } -var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, isSaveCgmSource) { +var determine_basal = function determine_basal(glucose_status, currenttemp, iob_data, profile, autosens_data, meal_data, tempBasalFunctions, microBolusAllowed, reservoir_data, currentTime, flatBGsDetected) { var rT = {}; //short for requestedTemp var deliverAt = new Date(); @@ -142,16 +142,14 @@ var determine_basal = function determine_basal(glucose_status, currenttemp, iob_ if (minAgo > 12 || minAgo < -5) { // Dexcom data is too old, or way in the future rT.reason = "If current system time "+systemTime+" is correct, then BG data is too old. The last BG data was read "+minAgo+"m ago at "+bgTime; // if BG is too old/noisy, or is changing less than 1 mg/dL/5m for 45m, cancel any high temps and shorten any long zero temps - //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc - } else if ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 && !isSaveCgmSource) { + } else if ( bg > 60 && flatBGsDetected) { if ( glucose_status.last_cal && glucose_status.last_cal < 3 ) { rT.reason = "CGM was just calibrated"; } else { rT.reason = "Error: CGM data is unchanged for the past ~45m"; } } - //cherry pick from oref upstream dev cb8e94990301277fb1016c778b4e9efa55a6edbc - if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && glucose_status.delta == 0 && glucose_status.short_avgdelta > -1 && glucose_status.short_avgdelta < 1 && glucose_status.long_avgdelta > -1 && glucose_status.long_avgdelta < 1 ) && !isSaveCgmSource ) { + if (bg <= 10 || bg === 38 || noise >= 3 || minAgo > 12 || minAgo < -5 || ( bg > 60 && flatBGsDetected )) { if (currenttemp.rate > basal) { // high temp is running rT.reason += ". Replacing high temp basal of "+currenttemp.rate+" with neutral temp of "+basal; rT.deliverAt = deliverAt; diff --git a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt index f02cc6cf5e..34c6253160 100644 --- a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt +++ b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSAMA/DetermineBasalAdapterAMAJS.kt @@ -162,7 +162,7 @@ class DetermineBasalAdapterAMAJS internal constructor(scriptReader: ScriptReader microBolusAllowed: Boolean, uamAllowed: Boolean, advancedFiltering: Boolean, - isSaveCgmSource: Boolean + flatBGsDetected: Boolean ) { this.profile = JSONObject() this.profile.put("max_iob", maxIob) diff --git a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt index bf0c69bc78..2adf294d8a 100644 --- a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt +++ b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/DetermineBasalAdapterSMBJS.kt @@ -57,7 +57,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: private var microBolusAllowed = false private var smbAlwaysAllowed = false private var currentTime: Long = 0 - private var saveCgmSource = false + private var flatBGsDetected = false override var currentTempParam: String? = null override var iobDataParam: String? = null @@ -79,7 +79,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed") aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed") aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime") - aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource") + aapsLogger.debug(LTag.APS, "flatBGsDetected: $flatBGsDetected") var determineBasalResultSMB: DetermineBasalResultSMB? = null val rhino = Context.enter() val scope: Scriptable = rhino.initStandardObjects() @@ -119,7 +119,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: java.lang.Boolean.valueOf(microBolusAllowed), makeParam(null, rhino, scope), // reservoir data as undefined java.lang.Long.valueOf(currentTime), - java.lang.Boolean.valueOf(saveCgmSource) + java.lang.Boolean.valueOf(flatBGsDetected) ) val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject scriptDebug = LoggerCallback.scriptDebug @@ -174,7 +174,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: microBolusAllowed: Boolean, uamAllowed: Boolean, advancedFiltering: Boolean, - isSaveCgmSource: Boolean + flatBGsDetected: Boolean ) { val pump = activePlugin.activePump val pumpBolusStep = pump.pumpDescription.bolusStep @@ -262,7 +262,7 @@ class DetermineBasalAdapterSMBJS internal constructor(private val scriptReader: this.microBolusAllowed = microBolusAllowed smbAlwaysAllowed = advancedFiltering currentTime = now - saveCgmSource = isSaveCgmSource + this.flatBGsDetected = flatBGsDetected } private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { diff --git a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt index b60afe4e66..861a0c7272 100644 --- a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt +++ b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMB/OpenAPSSMBPlugin.kt @@ -14,6 +14,7 @@ import info.nightscout.interfaces.aps.APS import info.nightscout.interfaces.aps.AutosensResult import info.nightscout.interfaces.aps.DetermineBasalAdapter import info.nightscout.interfaces.aps.SMBDefaults +import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.iob.IobCobCalculator @@ -55,7 +56,8 @@ class OpenAPSSMBPlugin @Inject constructor( private val sp: SP, private val dateUtil: DateUtil, private val repository: AppRepository, - private val glucoseStatusProvider: GlucoseStatusProvider + private val glucoseStatusProvider: GlucoseStatusProvider, + private val bgQualityCheck: BgQualityCheck ) : PluginBase( PluginDescription() .mainType(PluginType.APS) @@ -191,6 +193,7 @@ class OpenAPSSMBPlugin @Inject constructor( constraintChecker.isUAMEnabled(it) inputConstraints.copyReasons(it) } + val flatBGsDetected = bgQualityCheck.state == BgQualityCheck.State.FLAT profiler.log(LTag.APS, "detectSensitivityAndCarbAbsorption()", startPart) profiler.log(LTag.APS, "SMB data gathering", start) start = System.currentTimeMillis() @@ -207,7 +210,7 @@ class OpenAPSSMBPlugin @Inject constructor( smbAllowed.value(), uam.value(), advancedFiltering.value(), - activePlugin.activeBgSource.javaClass.simpleName == "DexcomPlugin" + flatBGsDetected ) val now = System.currentTimeMillis() val determineBasalResultSMB = determineBasalAdapterSMBJS.invoke() diff --git a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt index 5cf8d2412d..19ee62abfa 100644 --- a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt +++ b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/DetermineBasalAdapterSMBDynamicISFJS.kt @@ -59,7 +59,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri private var microBolusAllowed = false private var smbAlwaysAllowed = false private var currentTime: Long = 0 - private var saveCgmSource = false + private var flatBGsDetected = false override var currentTempParam: String? = null override var iobDataParam: String? = null @@ -81,7 +81,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri aapsLogger.debug(LTag.APS, "MicroBolusAllowed: $microBolusAllowed") aapsLogger.debug(LTag.APS, "SMBAlwaysAllowed: $smbAlwaysAllowed") aapsLogger.debug(LTag.APS, "CurrentTime: $currentTime") - aapsLogger.debug(LTag.APS, "isSaveCgmSource: $saveCgmSource") + aapsLogger.debug(LTag.APS, "flatBGsDetected: $flatBGsDetected") var determineBasalResultSMB: DetermineBasalResultSMB? = null val rhino = Context.enter() val scope: Scriptable = rhino.initStandardObjects() @@ -121,7 +121,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri java.lang.Boolean.valueOf(microBolusAllowed), makeParam(null, rhino, scope), // reservoir data as undefined java.lang.Long.valueOf(currentTime), - java.lang.Boolean.valueOf(saveCgmSource) + java.lang.Boolean.valueOf(flatBGsDetected) ) val jsResult = determineBasalObj.call(rhino, scope, scope, params) as NativeObject scriptDebug = LoggerCallback.scriptDebug @@ -176,7 +176,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri microBolusAllowed: Boolean, uamAllowed: Boolean, advancedFiltering: Boolean, - isSaveCgmSource: Boolean + flatBGsDetected: Boolean ) { val pump = activePlugin.activePump val pumpBolusStep = pump.pumpDescription.bolusStep @@ -312,7 +312,7 @@ class DetermineBasalAdapterSMBDynamicISFJS internal constructor(private val scri this.microBolusAllowed = microBolusAllowed smbAlwaysAllowed = advancedFiltering currentTime = now - saveCgmSource = isSaveCgmSource + this.flatBGsDetected = flatBGsDetected } private fun makeParam(jsonObject: JSONObject?, rhino: Context, scope: Scriptable): Any { diff --git a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt index 03eadbee1a..fc9b4207ee 100644 --- a/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt +++ b/plugins/aps/src/main/java/info/nightscout/plugins/aps/openAPSSMBDynamicISF/OpenAPSSMBDynamicISFPlugin.kt @@ -7,6 +7,7 @@ import info.nightscout.core.iob.iobCobCalculator.GlucoseStatusProvider import info.nightscout.database.impl.AppRepository import info.nightscout.interfaces.Config import info.nightscout.interfaces.aps.DetermineBasalAdapter +import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.iob.IobCobCalculator import info.nightscout.interfaces.plugin.ActivePlugin @@ -42,7 +43,8 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor( dateUtil: DateUtil, repository: AppRepository, glucoseStatusProvider: GlucoseStatusProvider, - private val config: Config + private val config: Config, + private val bgQualityCheck: BgQualityCheck ) : OpenAPSSMBPlugin( injector, aapsLogger, @@ -58,7 +60,8 @@ class OpenAPSSMBDynamicISFPlugin @Inject constructor( sp, dateUtil, repository, - glucoseStatusProvider + glucoseStatusProvider, + bgQualityCheck ) { init { diff --git a/plugins/main/src/main/java/info/nightscout/plugins/source/AidexPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/source/AidexPlugin.kt index 53efc33d2f..4a1a6f5aab 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/source/AidexPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/source/AidexPlugin.kt @@ -41,17 +41,11 @@ class AidexPlugin @Inject constructor( aapsLogger, rh, injector ), BgSource { - private var advancedFiltering = false - /** * Aidex App doesn't have upload to NS */ override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true - override fun advancedFilteringSupported(): Boolean { - return advancedFiltering - } - // Allow only for pumpcontrol or dev & engineering_mode override fun specialEnableCondition(): Boolean { return config.APS.not() || config.isDev() && config.isEngineeringMode() diff --git a/plugins/main/src/main/java/info/nightscout/plugins/source/DexcomPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/source/DexcomPlugin.kt index 5b6319982b..1217e047d5 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/source/DexcomPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/source/DexcomPlugin.kt @@ -27,6 +27,7 @@ import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginType import info.nightscout.interfaces.profile.Profile import info.nightscout.interfaces.source.BgSource +import info.nightscout.interfaces.source.DexcomBoyda import info.nightscout.plugins.R import info.nightscout.plugins.source.activities.RequestDexcomPermissionActivity import info.nightscout.rx.logging.AAPSLogger @@ -58,7 +59,7 @@ class DexcomPlugin @Inject constructor( .preferencesId(R.xml.pref_dexcom) .description(R.string.description_source_dexcom), aapsLogger, rh, injector -), BgSource { +), BgSource, DexcomBoyda { init { if (!config.NSCLIENT) { @@ -66,9 +67,7 @@ class DexcomPlugin @Inject constructor( } } - override fun advancedFilteringSupported(): Boolean { - return true - } + override fun advancedFilteringSupported(): Boolean = true override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = (glucoseValue.sourceSensor == GlucoseValue.SourceSensor.DEXCOM_G6_NATIVE || diff --git a/plugins/main/src/main/java/info/nightscout/plugins/source/NSClientSourcePlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/source/NSClientSourcePlugin.kt index 901c5c6aa8..0f2edb1158 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/source/NSClientSourcePlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/source/NSClientSourcePlugin.kt @@ -64,9 +64,7 @@ class NSClientSourcePlugin @Inject constructor( } } - override fun advancedFilteringSupported(): Boolean { - return isAdvancedFilteringEnabled - } + override fun advancedFilteringSupported(): Boolean = isAdvancedFilteringEnabled override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false diff --git a/plugins/main/src/main/java/info/nightscout/plugins/source/RandomBgPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/source/RandomBgPlugin.kt index baeb4b0c23..5fec0528bc 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/source/RandomBgPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/source/RandomBgPlugin.kt @@ -73,9 +73,7 @@ class RandomBgPlugin @Inject constructor( private val disposable = CompositeDisposable() - override fun advancedFilteringSupported(): Boolean { - return true - } + override fun advancedFilteringSupported(): Boolean = true override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = glucoseValue.sourceSensor == GlucoseValue.SourceSensor.RANDOM && sp.getBoolean(info.nightscout.core.utils.R.string.key_do_ns_upload, false) diff --git a/plugins/main/src/main/java/info/nightscout/plugins/source/XdripPlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/source/XdripPlugin.kt index fcf8ff8fce..a6b8d715dd 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/source/XdripPlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/source/XdripPlugin.kt @@ -43,9 +43,7 @@ class XdripPlugin @Inject constructor( override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = false - override fun advancedFilteringSupported(): Boolean { - return advancedFiltering - } + override fun advancedFilteringSupported(): Boolean = advancedFiltering private fun detectSource(glucoseValue: GlucoseValue) { advancedFiltering = arrayOf( diff --git a/plugins/support/src/main/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt b/plugins/support/src/main/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt index 85da3a2dfa..6a5c845cb1 100644 --- a/plugins/support/src/main/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt +++ b/plugins/support/src/main/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPlugin.kt @@ -7,9 +7,11 @@ import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.constraints.Constraints import info.nightscout.interfaces.iob.IobCobCalculator +import info.nightscout.interfaces.plugin.ActivePlugin import info.nightscout.interfaces.plugin.PluginBase import info.nightscout.interfaces.plugin.PluginDescription import info.nightscout.interfaces.plugin.PluginType +import info.nightscout.interfaces.source.DexcomBoyda import info.nightscout.plugins.support.R import info.nightscout.rx.AapsSchedulers import info.nightscout.rx.bus.RxBus @@ -33,10 +35,11 @@ class BgQualityCheckPlugin @Inject constructor( rh: ResourceHelper, private val rxBus: RxBus, private val iobCobCalculator: IobCobCalculator, - private val aapsSchedulers: + private val aapsSchedulers: AapsSchedulers, private val fabricPrivacy: FabricPrivacy, - private val dateUtil: DateUtil + private val dateUtil: DateUtil, + private val activePlugin: ActivePlugin ) : PluginBase( PluginDescription() .mainType(PluginType.CONSTRAINTS) @@ -49,13 +52,6 @@ class BgQualityCheckPlugin @Inject constructor( private var disposable: CompositeDisposable = CompositeDisposable() - enum class State { - UNKNOWN, - FIVE_MIN_DATA, - RECALCULATED, - DOUBLED - } - override fun onStart() { super.onStart() disposable += rxBus @@ -69,51 +65,83 @@ class BgQualityCheckPlugin @Inject constructor( disposable.clear() } - var state: State = State.UNKNOWN + override var state: BgQualityCheck.State = BgQualityCheck.State.UNKNOWN override var message: String = "" // Fallback to LGS if BG values are doubled override fun applyMaxIOBConstraints(maxIob: Constraint): Constraint = - if (state == State.DOUBLED) + if (state == BgQualityCheck.State.DOUBLED) maxIob.set(aapsLogger, 0.0, "Doubled values in BGSource", this) else maxIob - @Suppress("CascadeIf") fun processBgData() { + fun processBgData() { val readings = iobCobCalculator.ads.getBgReadingsDataTableCopy() for (i in readings.indices) // Deltas are calculated from last ~50 min. Detect RED state only on this interval if (i < min(readings.size - 2, 10)) if (abs(readings[i].timestamp - readings[i + 1].timestamp) <= T.secs(20).msecs()) { - state = State.DOUBLED + state = BgQualityCheck.State.DOUBLED aapsLogger.debug(LTag.CORE, "BG similar. Turning on red state.\n${readings[i]}\n${readings[i + 1]}") message = rh.gs(R.string.bg_too_close, dateUtil.dateAndTimeAndSecondsString(readings[i].timestamp), dateUtil.dateAndTimeAndSecondsString(readings[i + 1].timestamp)) return } - if (iobCobCalculator.ads.lastUsed5minCalculation == true) { - state = State.FIVE_MIN_DATA + if (activePlugin.activeBgSource !is DexcomBoyda && isBgFlatForInterval(staleBgCheckPeriodMinutes, staleBgMaxDeltaMgdl) == true) { + state = BgQualityCheck.State.FLAT + message = rh.gs(R.string.a11y_bg_quality_flat) + } else if (iobCobCalculator.ads.lastUsed5minCalculation == true) { + state = BgQualityCheck.State.FIVE_MIN_DATA message = "Data is clean" } else if (iobCobCalculator.ads.lastUsed5minCalculation == false) { - state = State.RECALCULATED + state = BgQualityCheck.State.RECALCULATED message = rh.gs(R.string.recalculated_data_used) } else { - state = State.UNKNOWN + state = BgQualityCheck.State.UNKNOWN message = "" } } + // inspired by @justmara + @Suppress("SpellCheckingInspection", "SameParameterValue") + private fun isBgFlatForInterval(minutes: Long, maxDelta: Double): Boolean? { + val data = iobCobCalculator.ads.getBgReadingsDataTableCopy() + val lastBg = iobCobCalculator.ads.lastBg()?.value + val now = dateUtil.now() + val offset = now - T.mins(minutes).msecs() + val sizeRecords = data.size + + lastBg ?: return null + if (sizeRecords < 5) return null // not enough data + if (data[data.size - 1].timestamp > now - 45 * 60 * 1000L) return null // data too fresh to detect + if (data[0].timestamp < now - 7 * 60 * 1000L) return null // data is old + + for (bg in data) { + if (bg.timestamp < offset) break + if (abs(lastBg - bg.value) > maxDelta) return false + } + return true + } + @DrawableRes override fun icon(): Int = when (state) { - State.UNKNOWN -> 0 - State.FIVE_MIN_DATA -> 0 - State.RECALCULATED -> R.drawable.ic_baseline_warning_24_yellow - State.DOUBLED -> R.drawable.ic_baseline_warning_24_red + BgQualityCheck.State.UNKNOWN -> 0 + BgQualityCheck.State.FIVE_MIN_DATA -> 0 + BgQualityCheck.State.RECALCULATED -> R.drawable.ic_baseline_warning_24_yellow + BgQualityCheck.State.DOUBLED -> R.drawable.ic_baseline_warning_24_red + BgQualityCheck.State.FLAT -> R.drawable.ic_baseline_trending_flat_24 } override fun stateDescription(): String = when (state) { - State.RECALCULATED -> rh.gs(R.string.a11y_bg_quality_recalculated) - State.DOUBLED -> rh.gs(R.string.a11y_bg_quality_doubles) - else -> "" + BgQualityCheck.State.RECALCULATED -> rh.gs(R.string.a11y_bg_quality_recalculated) + BgQualityCheck.State.DOUBLED -> rh.gs(R.string.a11y_bg_quality_doubles) + BgQualityCheck.State.FLAT -> rh.gs(R.string.a11y_bg_quality_flat) + else -> "" } + + companion object { + + const val staleBgCheckPeriodMinutes = 45L + const val staleBgMaxDeltaMgdl = 1.0 + } } \ No newline at end of file diff --git a/plugins/support/src/main/res/drawable/ic_baseline_trending_flat_24.xml b/plugins/support/src/main/res/drawable/ic_baseline_trending_flat_24.xml new file mode 100644 index 0000000000..6814353d02 --- /dev/null +++ b/plugins/support/src/main/res/drawable/ic_baseline_trending_flat_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/plugins/support/src/main/res/values/strings.xml b/plugins/support/src/main/res/values/strings.xml index cab629586f..fd881037b8 100644 --- a/plugins/support/src/main/res/values/strings.xml +++ b/plugins/support/src/main/res/values/strings.xml @@ -39,5 +39,6 @@ BG too close:\n%1$s\n%2$s recalculated double entries + Flat data. Considered to be wrong \ No newline at end of file diff --git a/plugins/support/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt b/plugins/support/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt index 6a688f40b4..6cd80f9448 100644 --- a/plugins/support/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt +++ b/plugins/support/src/test/java/info/nightscout/plugins/constraints/bgQualityCheck/BgQualityCheckPluginTest.kt @@ -6,14 +6,18 @@ import info.nightscout.androidaps.TestBase import info.nightscout.core.utils.fabric.FabricPrivacy import info.nightscout.database.entities.GlucoseValue import info.nightscout.interfaces.aps.AutosensDataStore +import info.nightscout.interfaces.bgQualityCheck.BgQualityCheck import info.nightscout.interfaces.constraints.Constraint import info.nightscout.interfaces.iob.IobCobCalculator +import info.nightscout.interfaces.plugin.ActivePlugin +import info.nightscout.interfaces.source.BgSource +import info.nightscout.interfaces.source.DexcomBoyda import info.nightscout.plugins.support.R import info.nightscout.rx.bus.RxBus import info.nightscout.shared.interfaces.ResourceHelper import info.nightscout.shared.utils.DateUtil import info.nightscout.shared.utils.T -import org.junit.Assert +import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.mockito.ArgumentMatchers.any @@ -28,10 +32,12 @@ class BgQualityCheckPluginTest : TestBase() { @Mock lateinit var fabricPrivacy: FabricPrivacy @Mock lateinit var dateUtil: DateUtil @Mock lateinit var autosensDataStore: AutosensDataStore + @Mock lateinit var activePlugin: ActivePlugin private lateinit var plugin: BgQualityCheckPlugin - val injector = HasAndroidInjector { AndroidInjector { } } + private val injector = HasAndroidInjector { AndroidInjector { } } + val now = 100000000L //private val autosensDataStore = AutosensDataStoreObject() @BeforeEach @@ -45,27 +51,29 @@ class BgQualityCheckPluginTest : TestBase() { iobCobCalculator, aapsSchedulers, fabricPrivacy, - dateUtil + dateUtil, + activePlugin ) `when`(iobCobCalculator.ads).thenReturn(autosensDataStore) `when`(rh.gs(anyInt())).thenReturn("") `when`(rh.gs(anyInt(), any(), any())).thenReturn("") + `when`(dateUtil.now()).thenReturn(now) } @Test fun runTest() { `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(null) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.UNKNOWN, plugin.state) - Assert.assertEquals(0, plugin.icon()) + Assertions.assertEquals(BgQualityCheck.State.UNKNOWN, plugin.state) + Assertions.assertEquals(0, plugin.icon()) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.FIVE_MIN_DATA, plugin.state) - Assert.assertEquals(0, plugin.icon()) + Assertions.assertEquals(BgQualityCheck.State.FIVE_MIN_DATA, plugin.state) + Assertions.assertEquals(0, plugin.icon()) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.RECALCULATED, plugin.state) - Assert.assertEquals(R.drawable.ic_baseline_warning_24_yellow, plugin.icon()) + Assertions.assertEquals(BgQualityCheck.State.RECALCULATED, plugin.state) + Assertions.assertEquals(R.drawable.ic_baseline_warning_24_yellow, plugin.icon()) val superData: MutableList = ArrayList() superData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = T.mins(20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) @@ -76,10 +84,10 @@ class BgQualityCheckPluginTest : TestBase() { `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.FIVE_MIN_DATA, plugin.state) + Assertions.assertEquals(BgQualityCheck.State.FIVE_MIN_DATA, plugin.state) `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.RECALCULATED, plugin.state) + Assertions.assertEquals(BgQualityCheck.State.RECALCULATED, plugin.state) val duplicatedData: MutableList = ArrayList() duplicatedData.add( @@ -136,8 +144,8 @@ class BgQualityCheckPluginTest : TestBase() { `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(true) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.DOUBLED, plugin.state) - Assert.assertEquals(R.drawable.ic_baseline_warning_24_red, plugin.icon()) + Assertions.assertEquals(BgQualityCheck.State.DOUBLED, plugin.state) + Assertions.assertEquals(R.drawable.ic_baseline_warning_24_red, plugin.icon()) val identicalData: MutableList = ArrayList() identicalData.add( @@ -194,19 +202,73 @@ class BgQualityCheckPluginTest : TestBase() { `when`(autosensDataStore.lastUsed5minCalculation).thenReturn(false) plugin.processBgData() - Assert.assertEquals(BgQualityCheckPlugin.State.DOUBLED, plugin.state) + Assertions.assertEquals(BgQualityCheck.State.DOUBLED, plugin.state) + + // Flat data + val flatData: MutableList = ArrayList() + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow + .FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 101.0, timestamp = now + T.mins(-10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-25).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 99.0, timestamp = now + T.mins(-30).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-35).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-40).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-45).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(flatData) + `when`(iobCobCalculator.ads.lastBg()).thenReturn(flatData[0]) + + // Test non-dexcom plugin on flat data + class OtherPlugin : BgSource { + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true + } + `when`(activePlugin.activeBgSource).thenReturn(OtherPlugin()) + plugin.processBgData() + Assertions.assertEquals(BgQualityCheck.State.FLAT, plugin.state) + Assertions.assertEquals(R.drawable.ic_baseline_trending_flat_24, plugin.icon()) + + // Test dexcom plugin on flat data + class DexcomPlugin : BgSource, DexcomBoyda { + + override fun shouldUploadToNs(glucoseValue: GlucoseValue): Boolean = true + } + `when`(activePlugin.activeBgSource).thenReturn(DexcomPlugin()) + plugin.processBgData() + Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state) + + // not enough data + val incompleteData: MutableList = ArrayList() + incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(0).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + incompleteData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-5).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + `when`(autosensDataStore.getBgReadingsDataTableCopy()).thenReturn(incompleteData) + `when`(iobCobCalculator.ads.lastBg()).thenReturn(incompleteData[0]) + `when`(activePlugin.activeBgSource).thenReturn(OtherPlugin()) + plugin.processBgData()// must be more than 5 values + Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 101.0, timestamp = now + T.mins(-10).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-15).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-20).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-25).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 99.0, timestamp = now + T.mins(-30).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-35).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + flatData.add(GlucoseValue(raw = 0.0, noise = 0.0, value = 100.0, timestamp = now + T.mins(-40).msecs(), sourceSensor = GlucoseValue.SourceSensor.UNKNOWN, trendArrow = GlucoseValue.TrendArrow.FLAT)) + plugin.processBgData() // must be at least 45 min old + Assertions.assertNotEquals(BgQualityCheck.State.FLAT, plugin.state) } @Test fun applyMaxIOBConstraintsTest() { - plugin.state = BgQualityCheckPlugin.State.UNKNOWN - Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) - plugin.state = BgQualityCheckPlugin.State.FIVE_MIN_DATA - Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) - plugin.state = BgQualityCheckPlugin.State.RECALCULATED - Assert.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) - plugin.state = BgQualityCheckPlugin.State.DOUBLED - Assert.assertEquals(0.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) + plugin.state = BgQualityCheck.State.UNKNOWN + Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) + plugin.state = BgQualityCheck.State.FIVE_MIN_DATA + Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) + plugin.state = BgQualityCheck.State.RECALCULATED + Assertions.assertEquals(10.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) + plugin.state = BgQualityCheck.State.DOUBLED + Assertions.assertEquals(0.0, plugin.applyMaxIOBConstraints(Constraint(10.0)).value(), 0.001) } } \ No newline at end of file